# tail-fin-core

Public session-lifecycle abstractions for [tail-fin](https://github.com/motosan-dev/tail-fin). The **stable API surface** that downstream agents (Flock A2A, external orchestration platforms) import — separated from the larger `tail-fin-common` so consumers don't pull in HTTP, CDP, and other internal helpers they don't need.

## What's here

| Type / Trait | Purpose |
|---|---|
| `Site` trait | Per-site lifecycle + identity contract (`id`, `display_name`, `cookie_domain_patterns`, `refresh_url`, `refresh`, `validate`, `attempt_login`, `detect_auth_failure`) |
| `SessionManager` | Single-account coordinator with `refresh` / `refresh_if_stale` / `validate` / `reload_cookies` |
| `SessionStatus` | `Valid` / `Degrading` / `Expired` / `Blocked` / `Unknown` enum |
| `Credentials` | `UsernamePassword` / `OAuth` / `CookieJar` / `Manual` — with `Debug` impl that masks secrets |
| `AuthFailureKind` | `CookieExpired` / `CredentialsRejected` / `RateLimited` / `AntibotBlock` / `AccountSuspended` |
| `FailureIndicators` | HTTP observation struct for `Site::detect_auth_failure` (status, body preview, URL, headers) |
| `SiteError` | Per-site variant of `TailFinError` (`ValidationFailed` / `RefreshFailed` / `ManualLoginRequired` / `AuthFailed`) |

Re-exports `night_fury_core::BrowserSession` so consumers only need to pull in `tail-fin-core`.

## Usage

```rust
use tail_fin_core::{Site, SessionStatus, SessionManager, BrowserSession};

async fn check_session<S: Site>(site: &S, session: &BrowserSession) -> anyhow::Result<()> {
    match site.validate(session).await? {
        SessionStatus::Valid => println!("{} session is valid", site.display_name()),
        SessionStatus::Expired => println!("refresh needed"),
        SessionStatus::Blocked { reason, retry_after } => {
            println!("blocked: {reason}, retry after {retry_after:?}");
        }
        _ => {}
    }
    Ok(())
}
```

Every tail-fin adapter (twitter, sa, reddit, …, tradingview) implements `Site`. The `tfd` daemon uses an `Arc<dyn Site>` registry to dispatch commands by site id.

## License

[MIT](../../LICENSE)
