7. Frame Types
All frames are JSON objects with a type field (string).
Implementations MUST silently ignore frames with unrecognised type values to allow forward
compatibility.
7.1 Frame Type Registry
| Type | Layer | Gated | Fields |
|---|---|---|---|
| handshake | 2 | No | nodeId (uuid), name (string), version (string), publicKey (base64url Ed25519), e2ePublicKey (base64 X25519), group (string), lifecycleRole (participant|validator|anchor), extensions (string[]) — full schema §20.1 |
| state-sync | deprecated | No | DEPRECATED (§2.7) — carried h1/h2; hidden state MUST NOT cross the wire. MUST NOT emit; ignore on receipt. |
| cmb | 3/4 | SVAF | timestamp (int), cmb (object: { key, createdBy, createdAt, fields, lineage, sig?, sigAlg?, group?, to? }) — full schema §20.2 |
| cmb-fetch | 3 | No | reqId (string), key (string content address), from (nodeId), timestamp — request the CMB stored under an exact content-address key (§15.8 re-verification of lineage roots). Serving is discretionary: a node MAY answer, and only with content it holds under that exact key. |
| cmb-fetch-result | 3 | No | reqId, key, found (bool), cmb (object|null; fields text-only — the address binds text, re-verifiers re-encode) — response to cmb-fetch. Self-verifying: the requester MUST recompute the content address and discard a mismatch (treated as a miss); an unsolicited result MUST NOT terminate a pending request. |
| role-grant | 3 | No | grantee (nodeId), role (validator|anchor), grantedBy (nodeId), grantedAt (int ms), granteeKey? (base64url), sig (base64url), sigAlg (ed25519). Confers a role; honored only when chain-resolved to the anchor (§6.6). |
| role-revoke | 3 | No | grantee (nodeId), grantedBy (nodeId), grantedAt (int ms), sig (base64url), sigAlg (ed25519). Withdraws a granted role; cascades on resolution (§6.6). |
| message | 2 | No | from, fromName, content, timestamp |
| mood | 7 | Drift | mood (string), context? (string) — application-layer mood broadcast; the receiver gates delivery on mood drift (§9.3 mood fast-coupling). Group-isolated per §5.8. |
| xmesh-insight | 6 | No | from, fromName, trajectory (float[6]), patterns (float[8]), anomaly (float), outcome (string), coherence (float), timestamp |
| peer-info | 2 | No | peers: [{ nodeId, name, wakeChannel?, lastSeen }] |
| wake-channel | 2 | No | platform (string), token (string), environment (string) |
| error | 2 | No | code (int), message (string), detail? (string) |
| ping | 2 | No | (no additional fields) |
| pong | 2 | No | (no additional fields) |
| relay-auth | 1 (relay) | No | nodeId (uuid), name (string), token? (string), wakeChannel? ({ platform, token, environment }) — client authenticates to a relay (§4.4.1). Transport-scope: exchanged with a relay, not a peer. |
| relay-peers | 1 (relay) | No | peers: [{ nodeId, name, wakeChannel?, offline }] — relay peer directory sent after authentication (§4.4.2). Transport-scope. |
| relay-ping / relay-pong | 1 (relay) | No | (no additional fields) — relay keepalive; clients MUST answer relay-ping with relay-pong (§4.4.5). Transport-scope. |
| relay-reauth | 1 (relay) | No | (no additional fields) — relay requests the client to re-send relay-auth (§4.4.6). Transport-scope. |
| relay-peer-joined | 1 (relay) | No | nodeId (uuid), name (string) — presence broadcast on the relay channel (§4.4.3). Transport-scope. |
| relay-peer-left | 1 (relay) | No | nodeId (uuid), name (string) — presence broadcast on the relay channel (§4.4.3). Transport-scope. |
All cognitive content — observations, decisions, feedback, directives — MUST be sent
as cmb frames. Only cmb frames
enter SVAF evaluation, produce anchor weights, and modulate CfC state.
The relay-* types are transport-scope (Section 4.4):
they are exchanged between a node and a relay, never between peers, and never reach the application
layer. The relay forwards peer frames as opaque payloads (Section 4.4.4) and does not originate any
of the peer-scope types above.
Deprecated — state-sync. The
state-sync frame carried a node’s hidden-state vectors
(h₁, h₂). Per the hidden-state locality invariant
(Section 2.7),
hidden state MUST NOT cross the wire. Implementations MUST NOT emit state-sync
and SHOULD ignore it on receipt. It is retained in this registry only to reserve the type and document the
deprecation; all peer influence flows through cmb frames evaluated by SVAF.
7.2 Error Frame
When a node encounters a protocol-level error, it SHOULD send an error frame
before closing the connection (if applicable). Error frames are informational — the receiving
node MUST NOT treat them as commands.
| Code | Name | Action | Description |
|---|---|---|---|
| 1001 | VERSION_MISMATCH | Close | Peer version is incompatible |
| 1002 | DIMENSION_MISMATCH | Reject frame | Vector dimension mismatch (legacy state-sync; deprecated — see §2.7) |
| 1003 | FRAME_TOO_LARGE | Close | Frame exceeds MAX_FRAME_SIZE |
| 1004 | HANDSHAKE_TIMEOUT | Close | No handshake within deadline |
| 1005 | DUPLICATE_NODE | Close | nodeId already connected |
| 2001 | SVAF_REJECTED | None | Memory-share rejected by SVAF (informational) |
Codes 1xxx are connection-level (close connection). Codes 2xxx are evaluation-level (informational). Error frames MUST NOT contain sensitive information.
7.3 Type Naming and Extensions
Frame types are identified by their type string value.
Core types (this specification) MUST NOT be redefined by extensions.
Extension types MUST use <extension>-<name> format.
Vendor types MUST use x-<vendor>-<name> format
and MUST be silently ignored by non-supporting nodes.
Q&A
Why MUST nodes silently ignore unknown frame types?
Without this rule, you can never add new features to the protocol. If a node crashes or rejects unknown frame types, then deploying a new extension (like mesh groups) requires upgrading every node on the mesh simultaneously — impossible in a peer-to-peer system. Silent ignore means old nodes and new nodes coexist: a node running a new extension sends its frames, and nodes that don’t support the extension simply ignore them. No crash, no error, the mesh keeps working. When a node adds support later, it handles the frame. No coordinated upgrade needed. This is the same principle used by HTTP (unknown headers ignored), TCP (unknown options skipped), and HTML (unknown tags ignored). Every successful protocol is evolvable because of this rule.
What happens if a relay receives an unknown frame type?
The relay forwards it. The relay is a dumb transport pipe — it wraps the payload in a { from, fromName, payload } envelope and sends it to the target or broadcasts it. It never inspects the payload type. This means extension frames (group, vendor, future types) flow through the relay without any relay changes. The intelligence is at the endpoints, not the transport.
Can an extension frame break an existing node?
No, if the node follows Section 7. The frame handler switches on msg.type. Unknown types fall through with no match and no action. The node’s cognitive state, memory, and coupling are unaffected. This is a hard requirement — implementations that reject or error on unknown types are non-conformant.