Commit Graph
3 Commits
Author SHA1 Message Date
octoturge d355955fb6 Add provision_printer and remove_printer to the gateway protocol
Adding a printer from the console has to put its LAN credential on the
gateway without the cloud keeping it. The new server messages carry it once
(documented as never-log) and are answered through the existing
command_result with the request id. The message cannot name a certificate
path or serial port: those are on the gateway's own disk and USB bus.
2026-10-29 15:03:27 +00:00
octoturge 1386b1b4db 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.
2026-10-19 00:32:57 +00:00
octoturge bb5a2680af Define the /ws/edge/v1 gateway uplink protocol
continuum-proxy and continuum-backend each had their own idea of what the
edge WebSocket carried, and the two did not overlap anywhere: the backend
expected an `auth` handshake the gateway never sends, telemetry shaped one
printer per message where the gateway sends a batch, and UUID printer ids a
gateway has no way to know. Neither end had a command path that matched the
other. Nothing failed, because nothing was written down for both sides to
mirror. This is that document.

schemas/continuum/v1/edge/ is canonical: client_message, server_message,
printer_telemetry and printer_command. packages/ts-types gets its first
real TypeBox mirror - the placeholder there said to add one "once something
actually consumes it", and continuum-backend's edge route now does. The
Rust mirror stays in continuum-proxy/src/uplink/protocol.rs rather than
packages/rust-types, since the gateway is its only Rust consumer.

Two decisions worth recording:

Field names are snake_case, unlike the backend's HTTP routes. The other end
is serde-derived Rust, and snake_case is what those structs produce without
a rename attribute on every field. It is also what the rest of this
repository already uses.

Optional fields accept an explicit null as well as being absent. A gateway
omits what it has no value for, which is what these schemas describe - but
serde emits null by default unless every field is annotated, and a single
such field would otherwise fail validation and take a whole farm's
telemetry batch down with it. Strict in what we send, liberal in what we
accept; the contract tests on both sides pin down each half.

Also bumps this package's @sinclair/typebox to ^0.33.12 to match what
continuum-backend already pins. It was on ^0.32.0, which resolved to a
separate 0.32.37 install - two TypeBox versions means two incompatible
TSchema identities, and Elysia rejected every schema from here on those
grounds alone.
2026-10-03 10:37:13 +00:00