Two protocols
magi's own wire is CBOR. The family's speaks JSON and CBOR both, and nothing is negotiated — a body says which encoding it is in its first byte.
Which wire is which
| wire | between | encoding |
|---|---|---|
| magi's own | a front end and its session, and a session and its own tool peers | CBOR, always, inside an envelope carrying a protocol version |
| the family | magi and casper, melchior, balthasar — and sessions with each other | JSON by default, CBOR on request; the reply comes back in whichever the call arrived in |
The framing is the same on both: a big-endian u32 byte count, then that many
bytes. Self-delimiting, so a peer that dies mid-frame is caught at the length rather than
misparsed deeper in.
Two shapes
A call expects an answer. An event does not, and nothing ever replies to one. The difference is whether anybody is waiting.
a call, and its reply → {"call":"recall","args":["how do we run the tests"]} ← {"ok":true,"family":1,"n":2,"result":[ … ]} an event. nobody replies. → {"event":"doing","busy":true,"working_for":7,"waiting":0}
The reply, field by field
| field | meaning |
|---|---|
| ok | whether the call was answered. A refusal is false with a zero exit status. |
| family | which revision of the wire this reply is written in. A newer one is refused by name; an older one is not. |
| n | how many values came back. |
| result | the values, always a list. A client that unpacks reads a bare-value server as having returned nothing at all, so the bug presents as an empty memory rather than as an error. |
| error | why not, when ok is false. |
| fault | which kind of no. Its absence means refused — the answer that costs a feature rather than a turn. |
Asking for CBOR
Every one-shot door takes --cbor; every socket answers in whatever it was
asked in. The three siblings agree byte for byte.
$ casper tools --cbor | xxd | head -1 00000000: a4 62 6f 6b f5 66 66 61 6d 69 6c 79 01 61 6e 01 {ok: true, family: 1, n: 1, …} $ balthasar needs --cbor | xxd | head -1 00000000: a4 62 6f 6b f5 66 66 61 6d 69 6c 79 01 61 6e 05 the same prefix
{ or [. CBOR's is a map or an array, whose first byte is 0x80–0xBF. The ranges do not overlap, so reading the encoding off the body is a reading rather than a guess — and a peer that has never heard of CBOR is unaffected.Refusals, and the other kind
A refused verb is {"ok":false,…} with exit status zero. A real error
arriving as “exited 1” is indistinguishable from the binary being missing, and the two
need different responses: one is a bug to report, the other is a sibling to carry on
without.
| fault | means | the caller should |
|---|---|---|
| refused | the verb was declined | carry on without that feature |
| unavailable | nothing answered — no socket, a dead socket, a connection that died | carry on without the sibling |
| failed | the write did not land | not assume what it handed over was recorded |
| malformed | the reply was not the shape the family agreed | report it — this is a bug, not a condition |