bus-conformance.md audits the flybus crate against bus-v1 sections 2 to 11, one row per normative sentence: requirement, status, code location, the test that proves it. 195 rows, 177 conforming, 10 deviating with the sentence that allows each, 1 must-fix (the teardown starvation, fixed in this branch) and 7 not implemented with an owner named. It also maps every BUS-01/02/03 acceptance bullet to its test, reconciles all ten of the crate's own differences from the draft, records the frame measurement with the reasons it is not a capacity claim, and lists the two contradictions inside the draft it will not guess at: the per-client queued byte budget against the latest-mode guarantee, and section 2's call budget against section 6's client-owned deadline. Neither changed any code. bus-v1 gains section 12: dated amendments for the three error codes the implementation needs and the draft left unnamed (CONFLICT, NO_TOPIC, ARTIFACT_MISMATCH), one line of reason each, and for the one operation it adds (rpc.responder.release), which is how bounded call correlation ends when a handler keeps reply authority after releasing the request delivery. Section 9's error list is inclusive, and section 4 already provides for a digest change. The crate README points at the report, carries the new measurement summary in place of the old numbers, and describes the two new test files; the design index and the flybus index point at the report too.
4.3 KiB
flybus: the communications bus
Status: crate landed, nothing wired onto it. Written 2026-09-22. Index only; the authority for the API and the wire format is the crate's own README, and the audit of the crate against the draft is the conformance report.
What it is
flybus is a local RPC and pub/sub bus for Tokio processes on one host, with immutable
file-backed artifacts for large payloads. One library, one router, one wire protocol:
messages are small strict-JSON envelopes carrying metadata and artifact references, while
bulk bytes (frames, audio, spike bitsets) live in a store directory the router owns.
Ownership follows deliveries and explicit holds; the router moves messages and tracks
ownership and does not interpret them. It implements the Flybus v1 draft (bus-v1, draft 1
of 2026-09-18) over the ipc-v1 scalar encodings.
Transports are an in-memory pair, an accepted AsyncRead + AsyncWrite stream, or a Unix
socket. Identity is bound by the launcher before Hello and checked against a Policy of
per-client grants; open/unbound mode is for trusted tests only and is not authentication.
Nothing in the crate is specific to a game, a brain or a stream.
What it is meant to replace
Today the three processes talk over two ad-hoc loopback surfaces:
| Today | Under the bus |
|---|---|
Feed: WebSocket 127.0.0.1:7400/feed, one binary message per snapshot at 30 Hz, header plus RGBA frame, audio and spike attachments, re-serialized per consumer |
One latest-mode topic per stream, with the frame as a sealed artifact shared by fan-out instead of copied per subscriber |
Control: loopback HTTP 127.0.0.1:7401 (/stimulate, /chat, /checkpoint, /pause, /status, ...) |
RPC services with per-client grants, FIFO dispatch, explicit cancel states and backpressure |
| Per-surface limits, rate limits and timeouts written twice | Limits and Policy in one place, negotiated at Hello |
The feed and control contracts in feed-protocol.md and control-api.md stay binding until a migration replaces them. The bus does not change any published contract by existing.
Crate layout
services/flysim/crates/flybus, a workspace member of the flysim workspace; no other crate
depends on it yet.
| Module | Contents |
|---|---|
wire |
Scalars, strict JSON, Envelope, ArtifactRef, Attachment, Location, framing, CONTRACT and contract_digest() |
error |
ErrorCode, Dispatch, BusError |
limits |
Limits and its hello encoding |
policy |
Policy, Grants, Pattern |
router |
Router, RouterConfig, RouterStats, UnixListenerHandle; the state machine in router/state.rs |
client |
Client and the handle types |
store |
The router-side file store and client-side location resolution |
transport |
Transport and the Stream trait |
Dependencies are already in the workspace lockfile: tokio, serde, serde_json, sha2,
libc. Tests run every integration case twice, once in memory and once over a Unix socket:
wire negotiation, RPC authority and cancellation, pub/sub credits and retention, artifact
allocate/seal/read with quotas and router restarts, plus the conformance suites and an
--ignored perf measurement.
Wiring still pending
- flysim publisher. Router startup inside the sim service, a store root under its runtime directory, and snapshot publication as artifact plus header envelope.
- flysim control services. The control endpoints as RPC services with grants, so the "no button endpoint" structural guarantee is expressed as a grant table.
- Stage and bridge clients. Both are TypeScript/Node; the crate is Rust only, so either a binding or a thin translating edge process is required before they leave the WebSocket and HTTP surfaces.
- Sizing.
max_store_bytes,max_retained_bytesandmax_latest_in_flightneed values chosen for 1.2 MB frames at 30 to 60 Hz with a slow consumer, not the defaults. - Lifecycle. Orphaned store directories are cleaned only when a new router starts on the same root, so service restart order and the store root's location need a decision.
- Migration order. The feed is the cheaper first move; control should follow only once the bus carries the feed in production for a full session.