Substrate (Phase 2)
Phase 2 lights up the local substrate — the layer that lets a worker start, store state, talk to a peer, load a plugin, and shut down cleanly without touching any cloud. The per-crate chapters land in plan 02-07; this page is the section landing.
What ships in Phase 2
rollout-proto— ownstransport.proto(heartbeat / control / work) andplugin.proto(sidecar).tonic-buildruns in this crate'sbuild.rs; every other crate consumes the generated code.rollout-storage— implementsStorage+StorageTxnon top ofredb. In-processtokio::sync::broadcastper-prefix forwatch(). Always-fsync. Default path./data/rollout.db.postcardvalue encoding.rollout-cloud-local— implementsObjectStore(content-addressed sharded FS under./data/object-store/),Queue(RAM hot path + spill torollout-storagefor restart replay),SecretStore(env-var allowlist, read-only), andComputeHint(Linux full via/proc+nvml-wrapper; macOS minimal stub viasysinfo).rollout-transport— implements gRPC with three logical channels (heartbeat / control / work). HTTP/2 +rustlsis the plan-of-record; QUIC viatonic-h3is feature-flagged EXPERIMENTAL.rollout-plugin-host— implementsPluginHostwith all three modes wired: Rust cdylib, PyO3 in-process, Python sidecar (gRPC-over-UDS).rollout-coordinator— minimal binary that registers workers, accepts heartbeats, persists worker registry + heartbeat ledger toStorage, and marks workers failed via deadline-based scan.- Smoke test —
make smoke+scripts/smoke.shspawn 1 coordinator + 2 workers + 1 cdylib + 1 Python sidecar, killw1, and assert deadline detection within2 × heartbeat_interval.
Plan-of-record vs stretch
HTTP/2 over tonic + rustls is the plan-of-record transport. QUIC via
tonic-h3 v0.0.x is feature-flagged
EXPERIMENTAL (it is currently pre-1.0 and bidi-streaming is not documented as
production-ready). The same .proto schema covers both; the swap-to-QUIC is a
single-config change in a later phase.
Trait surface
All trait contracts live in rollout-core. The Wave-0 extensions
(plan 02-00) align the rollout-core traits with the spec versions:
Storage/StorageTxn— see spec 04 §2. Phase 2 simplifiesscanto return ownedVecinstead ofBoxStream(object-safety +async_traitconstraint).PluginHost— see spec 03 §4–§5. Phase 2 usesVec<u8>payloads incall(); richer typed-payload helpers ship in later phases.Worker/Coordinator— see spec 01 §2.Worker::init/readyland in Phase 2;WorkerContextstays a unit struct until Phase 6.ObjectStore/Queue/SecretStore/ComputeHint— see spec 06 §3.Queue::ack/nack,ObjectStore::exists,ComputeHint::preemption_signal, andSecretStore::putship inrollout-corein Phase 2.EventEmitter— see spec 09 §2. The trait lives inrollout-core(plan 02-00); theStdoutJsonEmitterimpl lands inrollout-coordinator(plan 02-06).
Per-crate chapters
- Proto crate
- Storage
- Cloud-local
- Transport
- Plugin host
- Python bridge — PyO3 +
pyo3-async-runtimespin rationale - Coordinator
- Smoke test — the
make smokeSUBSTR-02 acceptance gate
Preflight
Before running make smoke, run bash scripts/preflight.sh. The script
checks that cargo, make, and python3 ≥ 3.11 are on PATH and notes
whether protoc is available (tonic-build bundles a copy when missing).