7. Frame Types — MMP Specification

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

The table is rendered from the normative machine-readable frame registry; its own shape is validated by frame-registry.schema.json.

Type Layer Gated Status Schema Fields
client-hello 2 No core handshake.schema.json First authenticated handshake offer.
server-hello 2 No core handshake.schema.json Server offer, negotiated extensions, transcript proof and key confirmation.
client-finish 2 No core handshake.schema.json Transcript hash, client proof and key confirmation.
cmb 3/4 SVAF core cmb-frame.schema.json Signed CAT7 record assertion.
cmb-encrypted 2/3 After decrypt core encrypted-cmb-frame.schema.json Directional ChaCha20-Poly1305 envelope with session, sequence, routing metadata and sealed record bytes.
control-encrypted 2 After decrypt core control-encrypted.schema.json Directional ChaCha20-Poly1305 envelope sealing one control frame; shares the session's per-direction sequence with cmb-encrypted.
cmb-anchors 4 Sealed only core control-frame.schema.json The first sealed frame a node sends on every Core Secure session it admits: the cognition keys of the sender's own records about to be replayed as context, or none (§9.4); changes no verification or admission.
cmb-fetch 3 No core cmb-fetch.schema.json Request a record by exact content address.
cmb-fetch-result 3 No core cmb-fetch-result.schema.json Correlation id with the keys returned and the keys not found; each returned record travels before it as its own cmb-encrypted frame.
authority-statement 3 Authority core authority-frame.schema.json One signed grant, revoke or endorse statement, identified by the hash of its canonical bytes; it counts only as §6.6 resolution places it.
authority-digest 3 No core authority-frame.schema.json The sender's authority root and in-force count, for anti-entropy (§6.6.8).
authority-fetch 3 No core authority-frame.schema.json Asks for statements by id, with their chains, or for a page of the responder's live set in authority order (§6.6.8).
authority-set 3 Authority core authority-frame.schema.json Answers an authority-fetch with statements in authority order; each is verified as gossip is, never on the session's word.
mood 4 Sealed only core control-frame.schema.json Session-scoped mood text and context, sealed in control-encrypted on a confirmed Core Secure session; attributed to the session's proven peer; never stored, relayed or remixed (§9.3).
room-join 2 Sealed only core room-join.schema.json Owner-signed room-join grant presented after the handshake; admits the session to a gated room when it binds the proven key (§5.8.1).
peer-info 2 No core control-frame.schema.json Authenticated-room peer and wake-channel gossip.
wake-channel 2 No core control-frame.schema.json Platform wake-channel registration.
error 2 No core control-frame.schema.json Protocol error code, message and optional bounded detail.
ping 2 No core control-frame.schema.json Peer keepalive request.
pong 2 No core control-frame.schema.json Peer keepalive response.
relay-auth 1 Relay auth transport relay-frame.schema.json Authenticate a node to a relay.
relay-peers 1 No transport relay-frame.schema.json Relay peer directory.
relay-ping 1 No transport relay-frame.schema.json Relay keepalive request.
relay-pong 1 No transport relay-frame.schema.json Relay keepalive response.
relay-reauth 1 No transport relay-frame.schema.json Request a fresh relay-auth frame.
relay-peer-joined 1 No transport relay-frame.schema.json Relay presence notification.
relay-peer-left 1 No transport relay-frame.schema.json Relay departure notification.
relay-error 1 No transport relay-frame.schema.json Relay-level error: message, with kind, code (the close that follows) and reason (§4.4.9).
role-grant legacy No legacy — Retired time-replayed role grant (sym 0.14.0 and earlier drafts). Confers nothing; MUST NOT emit; ignore on receipt. See §6.6.11 migration.
role-revoke legacy No legacy — Retired time-replayed role revoke with a cutoff (sym 0.14.0 and earlier drafts). MUST NOT emit; ignore on receipt. See §6.6.11 migration.
state-sync deprecated No legacy — Reserved legacy type. MUST NOT emit; ignore on receipt.
message legacy No legacy — Legacy unsigned application message.
xmesh-insight legacy No legacy — Legacy runtime projection.

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 registry distinguishes Core Secure wire types from retained legacy and runtime-extension types. A Core Secure participant MUST complete the three-frame authenticated handshake before it accepts cmb or cmb-encrypted. It MUST NOT silently downgrade to the legacy one-frame handshake or to an unsigned application frame.

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.

Sealed control frames. Only the frames a session protects are bound to it. A frame in clear can be read and injected by whatever sits on the path — over a relay, the relay itself and, before relay authentication proves keys, any client that can claim a nodeId there (§4.4.1, §4.4.4). In a Core Secure session that is CONNECTED (§5.3), a sender MUST therefore send every peer-scope frame other than ping and pong sealed: a record in cmb-encrypted, and every other frame — peer-info, wake-channel, mood, cmb-fetch, cmb-fetch-result, cmb-anchors (§9.4), room-join (§5.8.1), the four authority frames (authority-statement, authority-digest, authority-fetch, authority-set, §6.6.8), error and every negotiated extension frame — as the inner frame of a control-encrypted envelope (§18.2.1). A receiver MUST discard any of those frames that arrives in clear on a CONNECTED session, with one exception: it MAY read an error in clear, as the informational frame it always is (§7.2), but a clear error changes nothing. It closes no session, moves no sequence and changes no state, because whatever sits on the path can forge it, and a receiver cannot tell whether its sender still holds a session. The one clear error a node acts on is 1011 UNKNOWN_SESSION, which MAY prompt a new handshake and nothing else (§5.2.2). A sealed error is the peer’s own: one whose action is Close (§7.2) ends the session. ping and pong MAY travel in clear or sealed, and a receiver handles a sealed one as liveness, exactly as a clear one, and answers a sealed ping with a pong, in clear or sealed (§5.4). The inner frame of a control-encrypted envelope MUST NOT be a handshake frame (including the legacy handshake), cmb, cmb-encrypted, control-encrypted, state-sync or a relay-* frame. A sealed frame that opens advances its direction’s sequence (§18.2.1) whether or not its inner frame is then accepted; refusing the inner frame changes nothing else.

Session-bound frames speak only for the session’s peer. wake-channel, mood, cmb-fetch, cmb-fetch-result, cmb-anchors, room-join, authority-digest and authority-fetch take their sender from the session: the proven nodeId of the peer whose keys opened them. A receiver MUST attribute them to that nodeId and the name the handshake bound to it (§5.2). None of them carries a sender field, and a receiver MUST NOT take a sender from inside a frame. peer-info describes other nodes; its entries are hints. A receiver MAY use a gossiped wake channel to try a wake, but MUST NOT let it replace a wake channel the named node registered over its own session, and MUST NOT bind an identity or a key from it (§18.3). Sealing proves who sent a frame, not that what it says about third parties is true.

Author-signed frames carry their own authority. A frame that carries a signed statement takes its authority from that signature, never from the session that delivered it: a signed statement may be relayed over any session, and the session only transports it. Sealing it still keeps it from the relay, and from injection. The key it is verified against depends on what it is. An authority statement, carried in authority-statement or authority-set (§6.6.3), is verified by §6.6.9 and §18.3.2: against the subject key of the grant its authorisedBy names, or the pinned anchor keys, under the one verification rule, and never against a key a registry or a session binds to a nodeId. A record (§18.3.1), and an extension’s author-signed frame such as a sym-attest-v1 attestation (§16.4), is verified against the key the receiver binds to the nodeId it names (§3.4), never against the key of the session that delivered it.

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. A clear error changes nothing (§7.1). A sealed error is the session peer’s own notice: one whose Action is Close ends that session at the receiver too; any other sealed error is information.

CodeNameActionDescription
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 (Legacy Import only: Core Secure never refuses a session that proves the bound key, §3.4)
2001 SVAF_REJECTED None Memory-share rejected by SVAF (informational)
1006 AUTHENTICATION_FAILED Close Transcript signature or key confirmation failed
1007 ROOM_MISMATCH Close Authenticated room does not match the local room
1008 REPLAY_DETECTED Close Encrypted-frame counter repeated or moved backwards. Retained for compatibility: a receiver now discards a replayed frame and keeps the session (§18.2.1), so a conformant node does not send it
1009 IDENTITY_CONFLICT Close A nodeId bound to one identity key proved a different key (§3.4); recorded and reported. The refused side does not retry automatically
1010 SESSION_CLOSED Close The sender closed the session for a reason no other code names; the reason is in message (§5.2.2)
1011 UNKNOWN_SESSION None The sender holds no session for the frame it received; prompts the client to re-handshake, never closes a session (§5.2.2)

Codes 1xxx are connection-level: they concern a connection or a session, and most close it; the Action column says which. Codes 2xxx are evaluation-level (informational). Error-frame codes are a registry of their own, separate from the relay’s WebSocket close codes (§4.4.9): an implementation MUST NOT send an error frame with a code in the 4000–4999 range, which those close codes occupy. 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 rooms) 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 (room, 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.