@m4ike1/ion-protocol
v0.1.1
Published
Transport-neutral CBOR protocol for remote ion sessions
Maintainers
Readme
@m4ike1/ion-protocol
Transport-neutral CBOR protocol for remote ion sessions: routed envelopes, CBOR encoding, and byte-stream framing. Protocol version 8.
import {
PROTOCOL_VERSION,
encodeClientMessage,
ServerMessageDecoder,
type ClientHello,
} from "@m4ike1/ion-protocol";
const hello: ClientHello = { type: "hello", version: PROTOCOL_VERSION };
transport.send(encodeClientMessage(hello));
const decoder = new ServerMessageDecoder({ maxFrameLength: 1024 * 1024 });
for (const message of decoder.push(incomingChunk)) handleServerMessage(message);
decoder.end();How it fits together
Three layers, each usable on its own:
- Envelopes (
src/protocol.ts) —ClientMessage/ServerMessageunions. Routing, correlation ids, opaque strict-JSON payloads. Validated with TypeBox schemas plus a strict-JSON check; unknown object properties are rejected. - CBOR (
src/cbor/) — a strict, definite-length RFC 8949 subset.encodeCbor/decodeCbor, bounded byCborOptions. No tags, no indefinite lengths, no break markers, string-only map keys, no duplicate keys, finite numbers, safe integers only. - Framing (
src/framing.ts) — each frame is a four-byte unsigned big-endian payload length followed by one CBOR item.encodeFramebuilds frames;FrameDecodersplits arbitrary byte chunks back into payloads, tolerating any fragmentation or coalescing.
src/codec.ts composes all three: encodeClientMessage / encodeServerMessage validate, CBOR-encode, and frame in one step; ClientMessageDecoder / ServerMessageDecoder unframe, CBOR-decode, and validate incrementally via push(chunk) / end().
Messages
Client sends hello first ({ type: "hello", version }, any non-negative integer — the server negotiates). Then request ({ type, id, target, call }) and cancel ({ type, id, target }).
A server target is { serverId }; a session target is { serverId, sessionId, attachmentId }. serverId must be a canonical lowercase UUIDv4 (isServerId checks this).
Server replies with hello ({ type, version, serverId }, version must equal PROTOCOL_VERSION) or hello_error, correlated response (ok: true with optional result, or ok: false with { code, message }, code non-empty), service_update ({ subscriptionId, update }), and out-of-band attachment ({ attachment }, a session target or null) announcing this presentation's selected session route. Management attach() / detach() return no routing identifiers; the route arrives via attachment.
Payload boundary: call, result, and update are opaque strict JSON. Chord owns their semantics ({ serviceId, instance?, member, args } calls, $chord.service control vocabulary, subscription data, error codes); this package validates only that they are strict JSON — non-finite numbers, byte arrays, undefined, prototypes, and cycles are rejected. Parse them with @m4ike1/chord at the service adapter boundary. Session-directory state, transcripts, models, plugins, and all application values stay opaque service data. Server/worker lifecycle is outside this protocol.
Rules and limits
- Transports must preserve byte order. Peer authentication is not implemented by the experimental transport.
- Envelope violations, malformed CBOR, and invalid framing throw
ProtocolValidationErrorfrom the codec layer (FrameError/CborErrorunderneath, wrapped with a 500-char bounded message). Message decoders are sticky: after the first invalid frame, everypush/endthrows. - Schemas reject unknown object properties.
decodeCborrequires exactly one item — trailing bytes are an error. - Defaults: 16 MiB max per CBOR payload/frame (
DEFAULT_MAX_CBOR_BYTE_LENGTH,DEFAULT_MAX_FRAME_LENGTH), 1,000,000 array elements or map entries (DEFAULT_MAX_CBOR_CONTAINER_LENGTH), 64 nesting levels (DEFAULT_MAX_CBOR_DEPTH). FrameDecoder.end()throws on a truncated stream; callingpushafterend, or after failure, throws.maxFrameLengthmust be an integer in[0, 0xffff_ffff].- The protocol is experimental and has no compatibility guarantees. Use
isSupportedProtocolVersion(version)to gate onPROTOCOL_VERSION.
Further reading
docs/how-to.md— handshake, request/cancel/response loop, streaming decode, tuning limits.docs/reference.md— full API reference for envelopes, codec, framing, and CBOR.
