Fleet state: relationships, planes, and what replicates¶
Companion to ADR-0035. That ADR decided how fleet configuration converges (git as the state store; controller-mediated install and adopt; reconciliation over replication). This document finishes the design: the relationship model between the systems, and the file-class-by-file-class answer to "how do these files replicate."
Written generically per ADR-0032; the four-host reference instance appears only as illustration.
The gateway hierarchy¶
A fleet like this contains three kinds of gateway, and the design's load- bearing rule is about how layers bind to each other:
humans · agents · personal assistants · dashboards (CONSUMERS)
│ bind to STABLE NAMES only
┌───────────┴────────────┬──────────────────────┐
│ harness / LLM gateway │ realtime voice proxy │ CONSUMER GATEWAYS
│ (where intent enters) │ │ (ingress host / island)
└───────────┬────────────┴───────────┬──────────┘
│ capability aliases and named audio routes
┌───────────┴───────────────────────────────────┐
│ Capability Gateway (authenticated data plane) │ CAPABILITY GATEWAY
└───────────┬───────────────────────────────────┘
│ configured routes: aliases → tiers → endpoints
┌───────────┴───────────────────────────────────┐
│ serves (GPU hosts, native islands) │ SERVES
└───────────────────────────────────────────────┘
CONTROL PLANE: operator repository (git)
── config-install / config-adopt ──▶ node agents on every host
The binding rule: each layer references only the stable name of the layer below. Consumers hold gateway hostnames. Consumer gateways hold capability aliases or named routes. The Capability Gateway holds configured alias→tier→endpoint bindings for chat and deterministic named routes for audio and purpose models. Nothing skips a layer.
The payoff is measured, not theoretical. In the reference instance, one week
relocated the entire voice capability and its LLM across hosts twice. The
consumer that obeyed the rule (a personal assistant bound to llm.primary)
required zero changes. Every file that needed hand-editing during the moves —
a workbench env file, a proxy topology, a pipeline config — was a file
holding a direct URL, a layer-skipping binding. Replication problems are
mostly self-inflicted: the fewer files that hold anything but stable names,
the fewer files ever need to move.
Three planes, opposite physics¶
| Plane | Contents | Change rate | Failure mode to avoid |
|---|---|---|---|
| Intent | topology, capabilities, gateway/tier bindings, consumer bindings | days | wrong (stale-for-minutes is fine) |
| Observed | model status, health, versions, drift | seconds–minutes | stale (wrong-for-seconds is fine) |
| Data | live request paths (router relay, voice legs) | per-request | blocked on either other plane |
Intent is human-written and belongs in files under git. Observation is machine-derived and must never be written into shared files — a replicated health file is how a request routes to a dead backend, and a removed interpreter under a live CLI is invisible to any file sync but obvious to one version probe. Data-plane services read intent at start/reload only (config and process move together — several intent fields are fail-closed coupled to process flags) and learn liveness from their own probes with TTLs.
The replication matrix¶
| File class | Authority | Replicas | Mechanism | Trigger |
|---|---|---|---|---|
| Canonical topology + capability policies | operator repo (git) | per-host derived views, provenance-stamped | config-install |
operator, post-merge, confirm-gated |
| Host serve/voice manifests | operator repo ↔ live operator home | one live + one mirror | config-install / config-adopt |
operator |
| Router vocabulary | operator repo mirror | file layers + installed volume | domain installer owning the coupled reload | operator |
| Consumer bindings | operator repo (consumer registry) | one per consumer | config-install, consumer domain |
only when a gateway changes |
| Secrets | per-host env file | never replicated | host-side provisioning only | manual |
| Evidence | operator repo, append-only | none needed | captured after runs | per run |
| Model status | — | never a file | derived by inspection at query time | on query |
| Health | — | never a file | per-consumer probe caches with TTL | continuous |
| CLI artifacts | repo-named canonical pin (tag) | per-host installs | fleet upgrade, per-host strategy |
operator |
| Fleet history registry | operator-seat database | disposable read-model | scheduled read-only fan-out | cron |
Reading the matrix top to bottom: git carries the top four rows, with both arrows explicit (install makes the fleet match the repo; adopt makes the repo admit what the fleet became — a one-arrow system rots in the direction it cannot express). The bottom rows never replicate because they are questions, not files.
Roles in the reference instance¶
- Ingress host (gateway/operator, model-free): the harness/LLM gateway and tenant ingress (tailnet-scoped TLS names onto loopback services); designated fleet operator per ADR-0034.
- Serving host (gateway + heavy GPU): the Capability Gateway and the promoted primary serve.
- GPU host: serving capacity; capabilities move on and off it through the intent plane (its entire voice stack relocated without it losing its identity).
- Always-on evaluation host: node agent, scheduled read-only reporting
(
fleet version/fleet drift), evaluation jobs, and — as anative/unified/opportunisticnode — model-capable capabilities that are deliberately not promotion-eligible.
What a capability move touches (the checklist this design produces)¶
- Serve lifecycle on source and destination hosts (park, don't delete).
- The Capability Gateway's configured route bindings — via its domain installer, all layers.
- Consumer-gateway configs only if the gateway itself moved.
- Nothing else: consumers bound to names come along for free; observation discovers the new reality by inspection; evidence records it.
Every deviation from that short list is a file that skipped a layer — find it, and either bring it under the consumer registry or rebind it to a name.