# Chanora Server Prefetch Crate Design

Date: 2026-05-28

## Goal

Move server-resolution prefetch policy out of `chanora_core` into a focused Rust crate named `chanora_server_prefetch`, without changing connection behavior, Flutter APIs, or protocol dialing semantics.

## Non-Goals

- Do not change resolver behavior or DNS/SRV/TSDNS ordering.
- Do not change `ConnectConfig.resolved_address` semantics in `chanora_protocol`.
- Do not expose prefetch state in the UI.
- Do not prefetch bookmarks or additional hosts.
- Do not persist prefetched addresses.

## Architecture

Add a workspace member at `crates/chanora_server_prefetch`.

Responsibilities:

- Normalize server host keys by trimming and lowercasing.
- Track one active prefetch generation.
- Store at most one successful prefetched socket address.
- Reject stale async completions by generation.
- Return a prefetched address only for an exact normalized host match.
- Enforce the 2-minute freshness TTL.
- Resolve server addresses by calling `chanora_resolver::ChanoraResolver::resolve_client_address`.
- Log prefetch start, success, miss/stale, and failure diagnostics.

Dependencies:

- `chanora_resolver` for actual server address resolution.
- `tokio` for `Mutex` and spawned prefetch tasks.
- `tracing` for diagnostics.
- `thiserror` for a narrow `ServerPrefetchError` public error type.

## Public API

The crate exposes this small async owner type:

```rust
pub struct ServerPrefetcher { ... }

impl ServerPrefetcher {
    pub fn new() -> Self;
    pub async fn prefetch(&self, host: String) -> Result<(), ServerPrefetchError>;
    pub async fn fresh_match(&self, host: &str) -> Option<std::net::SocketAddr>;
}
```

`prefetch` returns after scheduling work, preserving the current invisible, non-blocking behavior. Empty normalized hosts are ignored successfully. Resolution failures are stored only as diagnostics and do not affect connect semantics.

## Core Integration

`chanora_core` replaces its private prefetch cache fields and helpers with `ServerPrefetcher`.

Core remains the trust boundary for connection config:

- It clears any caller-provided `ConnectConfig.resolved_address` before lookup.
- It asks `ServerPrefetcher::fresh_match` for the current host.
- It sets `dial_cfg.resolved_address` only from a fresh exact cache hit.
- It stores supervisor/reconnect config with `resolved_address: None`.

The Flutter bridge keeps calling the same core API, `prefetch_server_resolution(host)`. No Dart API change is intended.

## Data Flow

1. Flutter host editing schedules `ChanoraSession::prefetch_server_resolution(host)`.
2. Core delegates to `ServerPrefetcher::prefetch(host)`.
3. The prefetcher normalizes the host, increments generation, and spawns resolver work.
4. On success, the prefetcher stores the resolved socket address if the generation is still current.
5. On connect, core prepares `(stored_cfg, dial_cfg)`.
6. Core clears untrusted `resolved_address`, asks the prefetcher for a fresh exact match, and applies the result only to `dial_cfg`.
7. Protocol uses `dial_cfg.resolved_address` if present; otherwise it resolves normally.

## Error Handling

- Prefetch failures remain invisible to users.
- Prefetch failures are logged through `tracing`.
- If prefetch misses, is stale, or fails, connect falls back to normal protocol resolution.
- A caller-supplied `resolved_address` is never trusted by core.

## Testing

Move cache-policy tests from `chanora_core` into `chanora_server_prefetch`:

- fresh exact match returns the socket address.
- stale entries are ignored.
- different hosts are ignored.
- stale generation completions are ignored.
- blank normalized hosts return `Ok(())` and do not update generation or spawn resolver work.

Keep core tests for connection trust-boundary behavior:

- untrusted `resolved_address` is cleared on cache miss.
- prepared stored/supervisor config has `resolved_address: None`.
- prepared dial config can receive a fresh prefetched address.

Run at minimum:

- `cargo test -p chanora_server_prefetch --lib`
- `cargo test -p chanora_core --lib`
- `cargo test -p chanora_protocol --lib`

For final confidence, rerun the Android server connect smoke path that verifies prefetch logs and connected UI.

## Acceptance Criteria

- Workspace builds with the new crate member.
- `chanora_core` no longer owns the prefetch cache implementation.
- `chanora_server_prefetch` owns prefetch normalization, TTL, generation, storage, and resolver-backed warming.
- Public Flutter and Rust protocol behavior is unchanged.
- Existing server connect and reconnect safety tests pass.
- Android connect still reaches the connected server UI and does not get stuck in `Connecting` or `Synchronizing`.
