Held back until there was a job path to drive it; the queueing work needs it now. Three things are deliberate about its shape. It carries a presigned `url`, not the file's bytes. A sliced project runs to tens of megabytes and this is a JSON control channel, and a URL also keeps object-store credentials out of every gateway on every farm. The schema says to presign it at *dispatch* time rather than at job creation, because a queued job may wait days and a URL minted when it was queued would be long expired. It carries no filament or AMS field. The gateway reads the mapping out of the archive's own `Metadata/slice_info.config`, because the archive is the only thing that knows what each plate was sliced for. Passing one in would reintroduce the guess that made a real P1P heat to 250C and pause itself at 1%. And its description is blunt about what accepting it means, which is: very little. A full end-to-end run on 2026-10-15 was accepted, uploaded byte-perfect, reached RUNNING, and then the printer paused itself on a physical broken-filament fault. Job state has to be driven by the telemetry that follows, never by this reply - so the schema says so where a caller will read it, rather than leaving it to be rediscovered.
continuum-schemas
Single source of truth for the Continuum data model. Everything here is JSON Schema, compiled into two consumer packages:
packages/ts-types- hand-written TypeBox schemas undersrc/schemas/used for runtime validation incontinuum-backend's ElysiaJS routes.packages/rust-types- a Rust crate (continuum-rust-types) with hand-written,serde-derived structs forcontinuum-proxyandcontinuum-ai-worker.
Layout
schemas/continuum/v1/ Continuum's own model
ai/ continuum-backend <-> continuum-ai-worker: the
stream:camera_frames work queue and the
channel:print_alerts notification channel
edge/ continuum-backend <-> continuum-proxy: the
/ws/edge/v1 gateway uplink protocol
material/ printer/ domain objects
vendor.schema.json
schemas/bambulab/ Bambu Lab MQTT wire schemas (v1/v2, report + request)
packages/ts-types/ TypeBox schemas (hand-written, mirrors schemas/)
packages/rust-types/ serde structs (hand-written, mirrors schemas/)
The two inter-service protocols - edge/ and ai/ - are here for a
specific reason: each has two independent implementations, one per repo, and
when there was nothing written down between them they drifted completely
while every test passed. /ws/edge/v1 had an auth handshake the gateway
never sent; stream:camera_frames had a producer writing printerId and a
consumer reading printer_id, so every frame was attributed to a printer
called "unknown". Neither failure was visible from either side alone, which
is what a canonical schema plus a contract test on each end is for.
schemas/continuum/v1/*.schema.json is the canonical shape for each
domain object; packages/ts-types/src/schemas/ and
packages/rust-types/src/lib.rs both mirror it by hand. A mirror is added
per schema only once something actually consumes it, so not every file here
has one, and the two inter-service protocols keep their Rust mirror in the
single repo that consumes it rather than in packages/rust-types:
edge/ in continuum-proxy/src/uplink/protocol.rs, ai/ in
continuum-ai-worker/src/task.rs. The set is small and changes slowly,
which is why keeping hand-synced copies in sync is less overhead than a
codegen pipeline (protoc/ts-proto/prost-build) nothing actually
consumed - see git history for the
protobuf version this replaced, and why it was dropped: every service in
this system talks JSON (JSON Schema for the wire, TypeBox/serde for
runtime validation), so protobuf's actual value - a single schema compiled
into a compact binary wire format for both languages - never applied.
Whenever a domain schema changes, update all three by hand: the JSON Schema file, the TypeBox schema, and the Rust struct.
Build / verify
Requires npm install at the repo root, and a Rust toolchain, first.
npm install
npm run build:rust # cargo build, confirms the Rust structs compile
npm run typecheck # tsc -b, confirms the TypeBox schemas compile
There's no build:ts step - the TS side is hand-written, not generated.