|
| 1 | +# Cascade node protocol |
| 2 | + |
| 3 | +> Status: **design**. This document specifies the wire contract by which nodes |
| 4 | +> interoperate in a Cascade mesh, so that an independent implementation — in any |
| 5 | +> language — can be a first-class peer, not a client of a Cascade binary. The |
| 6 | +> protocol is the contract; no single codebase is. Parts marked **draft |
| 7 | +> (co-design)** are not yet implemented and are being designed jointly with the |
| 8 | +> first external implementation. |
| 9 | +
|
| 10 | +## Why a specified protocol |
| 11 | + |
| 12 | +Cascade's mesh is valuable beyond the Cascade binary: its device identity, |
| 13 | +authenticated transport, capability-token authorisation, content exchange, and |
| 14 | +(forthcoming) exec and op-log frames are exactly what another tool would |
| 15 | +otherwise reimplement to mesh with it. Rather than have such tools depend on the |
| 16 | +Cascade binary at runtime, they should be able to implement this protocol and |
| 17 | +join as peers. |
| 18 | + |
| 19 | +The hazard of two implementations is drift: an under-specified protocol degrades |
| 20 | +"interoperable" to "interoperable in theory" the first time one side relies on |
| 21 | +behaviour the other never promised. So three rules are load-bearing: |
| 22 | + |
| 23 | +1. **The wire is specified here, not by reference to the Rust code.** Behaviour |
| 24 | + that matters for interop is written down; the implementation conforms to the |
| 25 | + document, not the reverse. |
| 26 | +2. **Versioned, with a capability-negotiation handshake.** Nodes advertise a |
| 27 | + protocol version and the capability domains they support, and degrade |
| 28 | + gracefully — heterogeneous peers (one with capabilities the other lacks) are |
| 29 | + the normal case, not an error. |
| 30 | +3. **Conformance vectors are the forcing function.** A shared, language-neutral |
| 31 | + set of test vectors — handshake transcripts, token-verification cases, frame |
| 32 | + encode/decode fixtures — is executed by every implementation's CI. Two |
| 33 | + implementations that both pass the vectors stay compatible; documentation |
| 34 | + alone does not guarantee that. |
| 35 | + |
| 36 | +## Identity |
| 37 | + |
| 38 | +A node's identity is its **device ID**: the base32 encoding of the SHA-256 of its |
| 39 | +self-generated TLS certificate. All peer connections are TLS-encrypted and |
| 40 | +authenticated by device ID — a peer is exactly the holder of the private key for |
| 41 | +the certificate whose hash is its ID. This is the existing Cascade scheme; an |
| 42 | +interoperating implementation adopts it verbatim so identities are mutually |
| 43 | +verifiable. |
| 44 | + |
| 45 | +## Transport and handshake |
| 46 | + |
| 47 | +Peer connections are mutually-authenticated TLS. Reachability is governed by a |
| 48 | +single `DiscoveryReach` posture (`lan-only` / `private` / `public`); WAN peers |
| 49 | +traverse NAT via the opaque byte-pipe relay (HMAC-gated, payload-blind) and the |
| 50 | +rendezvous-by-presence path under the `public` posture. On connect, peers |
| 51 | +exchange a **handshake** carrying: |
| 52 | + |
| 53 | +- the protocol **version**, |
| 54 | +- the set of **capability domains** the node supports (see below), |
| 55 | +- identity proof (implicit in the TLS layer). |
| 56 | + |
| 57 | +A peer must not send frames for a capability domain the other did not advertise, |
| 58 | +and must reject (or quarantine) frames it does not understand rather than guess. |
| 59 | + |
| 60 | +## Capability domains |
| 61 | + |
| 62 | +A node advertises which of these it implements; the mesh is heterogeneous by |
| 63 | +design. |
| 64 | + |
| 65 | +| Domain | Frames | Status | |
| 66 | +| --------------- | --------------------------------------- | ----------------- | |
| 67 | +| `content` | block exchange (BEP-derived) | implemented | |
| 68 | +| `management` | `ManageRequest` / `ManageResponse` | implemented | |
| 69 | +| `exec` | process/PTY control + streams | draft (co-design) | |
| 70 | +| `oplog` | per-peer append-only log sync | draft (co-design) | |
| 71 | + |
| 72 | +A node that implements only `content` + `management` is a normal Cascade file |
| 73 | +node; one that adds `exec` can broker terminals and processes; one that adds |
| 74 | +`oplog` participates in a replicated operation log. None is required of all |
| 75 | +peers. |
| 76 | + |
| 77 | +## Authorisation |
| 78 | + |
| 79 | +Authority is a **capability grant** — a verb over a scope — held on a node and |
| 80 | +carried, between nodes, as a **signed capability token**. Tokens are signed by |
| 81 | +the issuing node's device key; a bearer presents one and the verifier checks |
| 82 | +signature, expiry, and revocation before authorising the carried grant through |
| 83 | +the same path an on-node grant takes. Delegation forms **bounded chains**: each |
| 84 | +hop can only narrow authority, never widen it, and a token's expiry is clamped to |
| 85 | +its parent's. Every authorised command is written to an append-only audit log. |
| 86 | + |
| 87 | +The dangerous verb classes — backend administration, node lifecycle, grant |
| 88 | +administration, and (when implemented) **exec** — are never satisfied by a |
| 89 | +node-wide grant; they require an explicit scope and a deliberate grant. See |
| 90 | +[`exec-capability.md`](exec-capability.md) for why exec sits in this tier. |
| 91 | + |
| 92 | +## Frame categories |
| 93 | + |
| 94 | +- **`content` — block exchange.** Content-addressed, immutable blocks. The |
| 95 | + substrate for file bytes; adaptive block sizes; last-write-wins per block for |
| 96 | + P2P-only folders. (Implemented.) |
| 97 | +- **`management` — control.** `ManageRequest` / `ManageResponse`: a verb command |
| 98 | + set (status, pin, cache, config, policy, backend, lifecycle, grant |
| 99 | + administration) dispatched into the same handlers the local CLI drives, gated |
| 100 | + by per-command authorisation and audited. (Implemented.) |
| 101 | +- **`oplog` — per-peer log sync (draft).** An operation log replicated as |
| 102 | + content: each peer's log is a **single-writer, append-only file**, so two |
| 103 | + peers' logs can never block-conflict, and distributing them is replication — |
| 104 | + consumers merge all peers' logs by a deterministic reduce. The unit is a |
| 105 | + per-peer log file, never one shared log file (whose per-block LWW would corrupt |
| 106 | + concurrent appends). The op shape, signing, and the reduce contract are the |
| 107 | + co-design items. |
| 108 | +- **`exec` — process/PTY control and streams (draft).** Control verbs travel as |
| 109 | + `management` frames; live stdin/stdout/stderr travel as **stream channels over |
| 110 | + the transport**, never through the content-addressed block store (a live stream |
| 111 | + is not immutable content). See [`exec-capability.md`](exec-capability.md). |
| 112 | + |
| 113 | +## Versioning and compatibility |
| 114 | + |
| 115 | +Every connection negotiates a protocol version and capability set at handshake. |
| 116 | +A version bump that changes a frame's meaning is gated by the conformance |
| 117 | +vectors: an implementation claiming a version must pass that version's vectors. |
| 118 | +Unknown capabilities and unknown frame types are ignored or quarantined per |
| 119 | +posture, never assumed. |
| 120 | + |
| 121 | +## Status |
| 122 | + |
| 123 | +Implemented today: identity, TLS transport, discovery/relay, capability tokens |
| 124 | +and delegation, `content`, and `management`. The `exec` and `oplog` domains are |
| 125 | +draft and co-designed with the first external peer implementation; this document |
| 126 | +is the place their wire shapes land before either side builds them. |
0 commit comments