Files
chanora/docs/superpowers/specs/2026-05-28-chanora-server-prefetch-crate-design.md
T

4.6 KiB

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_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_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:

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_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_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_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.