I’ve been playing with Cynitor this weekend and it has a lot of promise. I was able to get it working on an Arduino UnoQ which was neat.
Looking at how Cynitor’s server and dashboard communicate over a REST API (/api/...) and a WebSocket stream (/ws) and I’m wanting to have another level of formality here. Today the Python server is the specification, and WEBSOCKET_README.md describes it in prose. I want to write the protocol down as a versioned, machine-checkable specification built from five existing standards (see below). Then any server and any client (and any agent), in any language, can implement this protocol as a standard way to remotely monitor Cyphal systems.
This will also allow us to build an MCP server adapter in front of Cynitor servers which would unlock significant intellegence for Cyphal networks.
Proposal
| Layer | Standard | Describes |
|---|---|---|
| Data types | JSON Schema 2020-12 | Every payload, defined and shared by both API documents. |
| DSDL | Cyphal technical document | DSDL json interop |
| REST | OpenAPI 3.1 | Endpoints, parameters, status codes, auth |
| WebSocket | AsyncAPI 3.0 | /ws messages: who sends which, which ones get a reply |
| Errors | RFC 9457 Problem Details | The body of every HTTP error |
| Behaviour | RFC 2119 / RFC 8174 keywords | Semantics schemas can’t express: ordering, rates, filter matching |
We also would include RFC 6750 Bearer tokens, OAuth 2 scopes, RFC 9728 metadata for authentication and authorization in the protocol.
Core rules (draft)
- Versioning. The protocol has its own Semantic Version, independent of Cynitor’s release number. A server MUST report it as
protocol_version. A server MUST accept the WebSocket subprotocolcynitor.v<major>(RFC 6455 §1.9). A client MAY offer that subprotocol. - Extensibility. Receivers MUST ignore object members and WebSocket message
types they don’t recognise. Within a major version, senders MUST NOT remove a member or change its type or meaning. Adding members, message types or endpoints is a minor change. - WebSocket framing. Every WebSocket message MUST be a single JSON object in a text frame, with a string member
type. - Errors. Every HTTP
4xx/5xxresponse MUST carry an RFC 9457application/problem+jsonbody. Endpoint-specific details are extension members. A WebSocket error MUST be sent as{"type": "error", "error": "<message>"}, and the connection stays open. - Numbers. A floating-point value that is NaN or infinite MUST be encoded as
null, since JSON can’t represent those values. - Time.
timestampmembers MUST be RFC 3339 strings with a UTC offset. Members ending in_unixMUST be Unix seconds as a JSON number. - Mapping DSDL to JSON.
PROTOCOL.mddefines normatively how a decoded Cyphal value becomes JSON inattributes, for example that a port-ID bitmask becomes the list of IDs it sets. This part can’t be a fixed schema, because the types depend on the DSDL loaded at run time. - Deprecation. A deprecated member is marked
"deprecated": truein its schema and kept until the next major version. - Authentication and authorization. A server that requires authentication MUST accept RFC 6750 Bearer tokens in the
Authorizationheader. Every operation and every client WebSocket message type declares the scope it requires. A server MUST answer a request without valid credentials with401and one with insufficient scope with403, each with an RFC 9457 body and aWWW-Authenticateheader. Since browsers cannot set headers on WebSocket or link requests, a server MUST also accept a single-use ticket fromPOST /api/ticketsas theticketquery parameter. A server MUST close a WebSocket with code 4401 when its credential stops being valid.
References
- JSON Schema 2020-12: https://json-schema.org/draft/2020-12
- OpenAPI 3.1: https://spec.openapis.org/oas/v3.1.1.html
- AsyncAPI 3.0: https://www.asyncapi.com/docs/reference/specification/v3.0.0
- RFC 9457, Problem Details for HTTP APIs: https://www.rfc-editor.org/rfc/rfc9457
- RFC 2119 and RFC 8174 (BCP 14), requirement keywords: https://www.rfc-editor.org/rfc/rfc2119, https://www.rfc-editor.org/rfc/rfc8174
- RFC 6455, The WebSocket Protocol: https://www.rfc-editor.org/rfc/rfc6455
- RFC 3339, Date and Time on the Internet: https://www.rfc-editor.org/rfc/rfc3339