octoturgeandClaude Opus 5 ab9274497e Add WebRTC signalling to the edge protocol
A browser cannot signal to a gateway's go2rtc directly. Measured against
go2rtc 1.9.14: it sends no Access-Control-Allow-Origin on any response,
the SDP POST needs a preflight anyway because application/sdp is not a
CORS-safelisted content type, and it checks its API password before CORS -
so the preflight, which a browser always sends without credentials, gets
a 401. A gateway is behind a workshop NAT besides, so nothing can dial in.

So the cloud relays the SDP exchange over the socket the gateway already
holds open: webrtc_offer down, webrtc_answer or webrtc_error back up.

Signalling only - no video on this socket, and that is the design rather
than a limitation of it. Media goes browser-to-gateway through a TURN
relay, DTLS-SRTP encrypted, so neither the backend nor the relay can
read it. The alternative was tunnelling fMP4 up this socket and
streaming it from the API, which would have meant plaintext video
through the cloud and - since <video> cannot send an Authorization
header - a credential in a URL, the pattern /ws/telemetry rejected.

webrtc_error is its own variant rather than an empty answer because the
cloud holds a browser's request open on this, and a silent failure there
is a viewer watching a video element that never loads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-19 00:32:57 +00:00

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 under src/schemas/ used for runtime validation in continuum-backend's ElysiaJS routes.
  • packages/rust-types - a Rust crate (continuum-rust-types) with hand-written, serde-derived structs for continuum-proxy and continuum-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.

S
Description
Continuum platform: continuum-schemas
Readme
319 KiB
Languages
TypeScript 100%