octoturge 6ad400d44c Add a discovered_printers gateway message
A gateway can find printers on its own network and USB ports, but nothing
carried that back to the cloud, so adding one in the web app still meant an
operator reading an IP address and a serial number off a machine by hand.

Two schemas, both gateway -> cloud:

- `DiscoveredPrinter`, a candidate the gateway found but cannot drive,
  because it is not in that gateway's fleet file.
- A `discovered_printers` variant on the client message, carrying a whole
  batch that replaces what that gateway last reported. A printer that has
  gone is reported by its absence, and a gateway seeing nothing sends an
  empty list.

A candidate deliberately carries no credential, and cannot: a Bambu access
code is on the printer's screen and a PrusaLink API key is in its web UI, so
no probe learns either. What this buys is the address and the identity, which
are the parts an operator would otherwise have to go and look up.

Two details worth keeping in mind downstream:

`ambiguous_with` exists because PrusaLink serves OctoPrint's API, so a probe
that saw only that API cannot honestly claim PrusaLink. The candidate says
`octoprint` and names the alternative, and the operator settles it.

Every string here was chosen by a device on the customer's network - anything
answering an SSDP query or an HTTP probe names itself - so they are untrusted
display strings. Hence the length caps, and hence `DiscoveredPrinter` being
closed where its sibling variants are open: an unbounded LAN-supplied `name`
should not reach a database column or a React tree.
2026-10-19 08:04:27 +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%