Files
continuum-schemas/schemas/continuum/v1/edge/server_message.schema.json
T
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

56 lines
1.9 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.octoturge.com/Continuum/continuum-schemas/raw/branch/main/schemas/continuum/v1/edge/server_message.schema.json",
"title": "EdgeServerMessage",
"description": "What continuum-backend sends back down the same WebSocket. Discriminated on 'type'; snake_case for the same reason as EdgeClientMessage.",
"oneOf": [
{
"type": "object",
"title": "HelloAck",
"properties": {
"type": { "const": "hello_ack" },
"session_id": { "type": "string" }
},
"required": ["type", "session_id"]
},
{
"type": "object",
"title": "HelloError",
"description": "The handshake was rejected. The connection closes immediately after this.",
"properties": {
"type": { "const": "hello_error" },
"message": { "type": "string" }
},
"required": ["type", "message"]
},
{
"type": "object",
"title": "HeartbeatAck",
"properties": {
"type": { "const": "heartbeat_ack" },
"seq": {
"type": "integer",
"minimum": 0,
"description": "Echoes the seq from the heartbeat being acknowledged."
}
},
"required": ["type", "seq"]
},
{
"type": "object",
"title": "Command",
"description": "Do something to one printer on this gateway. Answered with a client command_result carrying the same command_id.",
"properties": {
"type": { "const": "command" },
"command_id": { "type": "string" },
"printer_id": {
"type": "string",
"description": "The gateway's own printer id, not the backend UUID - the backend translates on the way out, so a gateway never has to know its printers' cloud identities."
},
"command": { "$ref": "./printer_command.schema.json" }
},
"required": ["type", "command_id", "printer_id", "command"]
}
]
}