@crow-agent/protocol
v0.2.0
Published
Compact binary wire protocol: codec and framing for Crow's client/server channel.
Readme
@crow-agent/protocol
The wire layer for Crow's client/server protocol: length-prefixed framing, CBOR encoding, and validated message envelopes. It is runtime-neutral — no Node APIs, no sockets — and it knows nothing about the application payloads it carries.
Layers, bottom to top
- Framing. Each frame on the wire is a four-byte unsigned
big-endian length followed by exactly one CBOR item.
framing.tssplits an arbitrary byte stream into frames and rejects anything malformed. - CBOR.
encodeCbor/decodeCborhandle definite-length items only, with bounded sizes and nesting depth. - Envelopes.
protocol.tsdefines the versioned message grammar: the hello handshake, routed requests and responses, cancellations, subscription updates, and out-of-band attachment notices. Every schema rejects unknown object properties. - Opaque payloads. Service calls, results, and errors travel as strict-JSON values the protocol checks but never interprets. Chord owns their grammar; this package only guarantees they are JSON.
A minimal exchange
import {
PROTOCOL_VERSION, // 8
ServerMessageDecoder,
encodeClientMessage,
type ClientHello,
type ClientMessage,
} from "@crow-agent/protocol";
const hello: ClientHello = { type: "hello", version: PROTOCOL_VERSION };
sendBytes(encodeClientMessage(hello));
const decoder = new ServerMessageDecoder({ maxFrameLength: 1024 * 1024 });
function onBytes(chunk: Uint8Array) {
for (const message of decoder.push(chunk)) {
routeServerMessage(message); // your dispatch
}
}
decoder.end(); // throws if the stream ends mid-frameencodeClientMessage() / encodeServerMessage() validate and encode a
complete frame in one call. The decoders tolerate any fragmentation or
coalescing the transport hands them.
Routing model
A request names its destination explicitly: a server target carries
{ serverId }, a session target carries
{ serverId, sessionId, attachmentId }. The triple fences each call to
one logical server, one durable session, and one live attachment.
attach() / detach() return no routing identifiers themselves; the
server announces the live route out of band, and disconnecting releases
only that attachment once admitted calls settle.
What it refuses
Anything that is not strict JSON in an opaque payload — non-finite
numbers, undefined, byte arrays, prototypes, cycles — is rejected, as
are envelope violations, malformed CBOR, and bad framing. All of these
surface as ProtocolValidationError. Transports must preserve byte
order; the protocol itself does no peer authentication.
Default bounds: 16 MiB per CBOR payload/frame, 1,000,000 array elements or map entries, 64 nested item levels.
This protocol is experimental and carries no compatibility guarantees. Server and worker lifecycle are deliberately outside it — each replaceable server process owns its private lifecycle protocol, and the coordinator on the wire is only an opaque message router.
