phux docs
Decisions

0097 — Durable coordinator is a separate bounded endpoint

Durable work uses an independently versioned, authenticated phux-coordinator/1 endpoint with opaque identities, fenced authority epochs, idempotent...

stable document
Full source summary

Durable work uses an independently versioned, authenticated phux-coordinator/1 endpoint with opaque identities, fenced authority epochs, idempotent operation results, bounded snapshots, and credit-controlled events. It references Terminals without carrying terminal output or input. Clients that only speak the terminal protocol remain compatible, and missing or downgraded coordinator capabilities fail closed rather than falling back to L1 or L3.

Status: Accepted (forward-compat) Date: 2026-09-10

Context

ADR-0092 assigns durable Objectives, Runs, WorkSessions, Artifacts, Signals, bindings, event order, and evidence to one coordinator, but leaves its endpoint and recovery contract open. ADR-0092 is still Proposed; this ADR accepts the endpoint and recovery contract that any coordinator admitted under it must satisfy. ADR-0030, as generalized by ADR-0102, closes terminal synchronization to structured product state; ADR-0061 makes major.minor mismatch a hard refusal and additive growth capability-gated. Putting work frames in L1 violates the first rule; spending its version on coordinator evolution violates the second. The new plane must also survive lost replies, restarts, stale restored writers, slow subscribers, and retention gaps without duplicate work, unbounded memory, or split-brain authority.

Decision

Endpoint and compatibility

The coordinator is a distinct application endpoint selected before decoding: service phux-coordinator, label phux-coordinator/1, and an independent 0.1.0 version/capability namespace. v0.1 is paired-only, including its dedicated owner-only Unix socket; QUIC uses the label as ALPN. SSH/stdin and proofless local modes are refused. Terminal streams never multiplex coordinator frames. Endpoint absence permits terminal-only operation, never authoritative work state in L1, L3, AgentEvent, or a client.

Identity and authority

Every durable object/operation ID is opaque nonzero 16 bytes. CoordinatorId survives restart; IncarnationId changes each start. Each activation advances a checked durable epoch and acquires a short-lived certificate from an external, non-clonable witness that durably enforces the last authority epoch and one fencing token per CoordinatorId. Its signed certificate is authenticated in the handshake, and every request guards on it. The store lock excludes only within one image; a copied/restored store cannot bind or commit without the witness lease. Lease loss fences the writer; restore waits for revocation or expiry of the prior lease.

Authentication and scopes

Every connection uses ADR-0098’s endpoint-neutral phux-workload/v1 profile in workload-auth.md, domain-separated by service phux-coordinator. Principal, authority proof, fresh nonce, expiry, and scopes are connection-bound; terminal pairing or reachability grants nothing. Scopes are a closed bitset for work read, work command, terminal control, artifact read, Signal acknowledgement, and administration. Unknown bits are refused. The server intersects an authenticated unexpired grant and rechecks it before each mutation or delegated action. No credential or secret enters argv, env, diagnostics, or logs.

Commands and results

Every mutation carries OperationId, authority guard, and typed revision guards. Idempotence is keyed only by (CoordinatorId, OperationId); cross-principal reuse conflicts, as does a changed semantic digest. Admission and result state are transactional. A delegated terminal effect also has a durable outbox EffectAttemptId; a target with stable same-payload dedupe returns one result. If target dedupe cannot prove the outcome after a crash, the operation becomes INDETERMINATE and is never redispatched automatically. Lost replies use lookup and the same ID, never a blind replacement. Bounded result tombstones outlive state they created.

Snapshots, events, and bounds

Only a complete whole-authority, scope-filtered snapshot may reserve a v0.1 subscription. Snapshot/replay cursors are opaque, at most 4 KiB, and bound to principal, authority guard, and a hard-lived lease. One visibility predicate governs snapshots, events, replay, and lookup. Count/byte credit and server/principal quotas bound every cut and queue before allocation. GAP pauses deltas; a digest-verified replay plus ACK resumes the same subscription, or a fresh snapshot replaces it. Silent eviction is forbidden.

Terminal binding

A WorkSession binds through immutable BindingId to authenticated terminal-owner authority, incarnation, and exact L1 ResourceId of a Terminal-kind resource. Replacement creates a new binding; ResourceId, PID, title, cwd, host text, layout, StreamId, and BootstrapId are never durable work identity. Sourced facts admit exact-next owner sequence, replay only byte-identical duplicates, record explicit gaps, and refuse after binding end. Terminal output, input, leases, signals, bootstrap, and history remain on the terminal endpoint. The full contract is coordinator.md.

Why

A separate endpoint preserves ADR-0030 exactly where it is load-bearing: a terminal-only peer never learns work vocabulary and terminal synchronization never becomes a product event bus. Independent versions and permanent capabilities apply ADR-0061 without turning every coordinator addition into a terminal fleet break. Durable operation records make an ambiguous network reply safe, while epoch fencing and immutable binding history keep restart and restore from manufacturing false continuity.

Credit on both count and bytes is necessary: either dimension alone admits an unbounded queue of tiny events or a single oversized event. Complete snapshot cuts plus explicit GAP make every client state either provably current or visibly stale; there is no plausible-looking partial state.

Tradeoffs

  • Clients that use both surfaces maintain two authenticated connections and two version/capability state machines.
  • The store carries bounded operation/outbox tombstones, cuts, and retention metadata in addition to work facts.
  • Availability depends on a small external fencing witness; it holds activation sequence/token only, never work state or coordinator policy.
  • Indeterminate non-deduplicated target effects require reconciliation rather than unsafe automatic retry.
  • Fixed aggregate bounds may refuse legitimate load; callers split records or store bulk Artifact content instead of raising local limits.
  • Initial federation remains one home lineage plus sourced terminal facts.

Alternatives

Add a work tier to the terminal protocol. Rejected: it contradicts ADR-0030, makes terminal-only clients parse product state, and couples unrelated upgrade domains.

Store work in L3 metadata. Rejected: last-write-wins metadata cannot provide transactional operation deduplication, immutable order, authority epochs, complete snapshots, or evidence gaps.

Let Cockpit coordinate locally. Rejected by ADR-0092: it creates a second writer and loses authority whenever one UI is absent.

Use unbounded streaming over a reliable transport. Rejected: reliability does not bound producer/consumer skew, retained snapshot state, or allocations, and cannot signal application-level retention gaps.

View exact source