Skip to content

PRD: Fleet thin client — local shell, remote data #792

Description

@aarontrowbridge

PRD — Fleet thin client: local shell, remote data

Important

Problem

In fleet mode the panel is an iframe served by the hub: every UI asset, API call, and SSE handshake crosses the WAN link. On high-latency links (plane wifi, 750 ms RTT) the UI is structurally unusable — ~2.5 s per request against a 1.5 s attach budget (#777) — and the hub's request surface absorbs every client's render traffic, feeding the reconnect-storm class of incident (2026-09-03 wedge, §3.1).

Approach

Local shell, remote data. The client's vendored binary already ships the complete web app; in fleet mode it serves the UI locally and proxies the data plane (REST API + the two event streams) to the canonical hub, injecting fleet auth. The hub degrades to what it should be: a session store plus streams. The Sessions dropdown lists hub sessions natively (supersedes #779, which is held).

Approaches Considered

  • Relay mode in the local vendored server (chosen) — the server already owns HTTP, static assets, and SSE; one upstream connection per event endpoint (the front-door splitter pattern, mirrored client-side); extension-host reloads become relay-internal reattaches, never hub connection churn.
  • Extension-host proxy — zero fork work, but wedges a long-lived SSE multiplexer into the extension host process, which restarts on every window reload (churn = the wedge's favorite food).
  • Hub-side-only fixes (front door, budgets) — necessary but cannot fix render-over-WAN; the UI itself must move local.

Scope

In: a relay mode in the local server (serve app + path-preserving data proxy + auth injection); extension changes to spawn the local server in relay mode on fleet clients and point the panel at localhost; posture reporting (ties into #780's attach-state file); honest degraded state when the hub is unreachable.
Out: any session-data migration or mirroring (stores stay where they are); offline standalone continuity (deferred — "sessions-to-go" export is the future candidate); changing the hub front door; changes for standalone (non-fleet) machines; the hub-hosted agent-session containment question (§3.5).

Assumptions / Open Qs

  • Assumed the app's data fetches are same-origin relative paths (SDK baseUrl is configurable — a same-origin relay needs zero app changes; to be verified in the first slice).
  • Open: does relay mode fall back to the local store when the hub is unreachable (a "degraded but attached" posture), or fail honestly? Recommendation: fail honestly in v1 — dual-store fallback creates the confusion Read-only Fleet Sessions view: list hub sessions from a standalone client #779 was filed to avoid; revisit with sessions-to-go.
  • Open: SSE chain length (app → relay → tunnel → front door → server) — acceptable? The relay holds one upstream per endpoint, so it composes with the splitter rather than multiplying connections.

User Stories

  1. Plane wifi (the origin incident): Aaron opens Amicode on the MacBook over 750 ms wifi; the UI renders instantly from localhost; session data loads through the relay; SSE events arrive ~750 ms late but the studio is fully usable.
  2. Back at the hub: at home (26 ms), the same posture works with imperceptible latency; nothing changes because the link got faster.
  3. Hub goes down: the panel renders locally, then shows the honest posture (from Surface machine posture (hostname, hub reachability) in agent context #780's attach-state file): "canonical hub unreachable — data plane down." No fake local session list, no parked state machine.
  4. Reload the window: the extension host restarts; the relay reattaches its upstreams; the hub sees group joins, not a connection storm.

Modules & Interfaces

  • Fork (harmoniqs/opencode): relay mode on serve — flag takes the hub base URL; serves the app's static assets from the local binary; proxies data-plane paths (API + /event + /global/event) to the hub; injects fleet auth on proxied requests; SSE fan-out holds one upstream per event endpoint. Path-preserving; no app changes.
  • Extension (this repo): fleet-client attach flow spawns the local binary in relay mode (reusing ServerManager lifecycle + the guard), points the panel iframe at the local server instead of the tunnel URL; the tunnel remains the transport under the relay. Posture (Surface machine posture (hostname, hub reachability) in agent context #780) gains a relay mode state.
  • Auth: the relay injects the fleet token / per-boot password on the data plane; the app and the panel never handle credentials (Credential portability: take/push provider creds between client and hub over SSH #782's key-handling rule applies).

Testing Decisions

  • Relay integration test: boot a real local server in relay mode against a stub hub; assert UI served locally, API proxied path-preservingly, auth injected, SSE joined/reattached once.
  • Churn test: N extension-host restarts against a counting stub hub → exactly 2 SSE upstreams total (one per endpoint), not 2N.
  • Version-skew gate: relay refuses to start when client and hub pins disagree beyond the drift-gate tolerance (the brainstorm's pin-parity question, answered mechanically).

Risks

Source

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions