5. Layer 2: Connection
5.1 Discovery
Nodes MUST advertise via DNS-SD with service type _sym._tcp in the local. domain. The instance name MUST be the node’s nodeId.
TXT record fields:
| Key | Required | Value |
|---|---|---|
| node-id | MUST | Node UUID |
| node-name | MUST | Human-readable name |
| hostname | SHOULD | Machine hostname |
To prevent duplicate connections, the node with the lexicographically smaller nodeId MUST initiate the outbound TCP connection. The other node MUST NOT initiate.
5.2 Handshake
Upon connection, both sides MUST exchange the following frames in order:
1. handshake { type: "handshake", nodeId: "<uuid>", name: "<name>",
version: "0.2.0", extensions: [] }
2. state-sync { type: "state-sync", h1: [...], h2: [...], confidence: 0.8 }
3. peer-info { type: "peer-info", peers: [...] } [if known]
4. wake-channel { type: "wake-channel", platform, token, env } [if configured]- —The
versionfield MUST be the MMP specification version the node implements (e.g.,"0.2.0"). Nodes SHOULD accept peers with the same major version. Nodes MAY reject peers with incompatible versions. - —The
extensionsfield SHOULD list supported protocol extensions (e.g.,["consent-v0.1"]). Nodes MUST ignore unrecognised extensions. - —The inbound node MUST wait for a
handshakeframe as the first frame. If any other frame type arrives first, or no handshake arrives within 10,000 ms, the connection MUST be closed. - —If a node receives a handshake with a nodeId that is already connected, the new connection MUST be closed (duplicate guard).
5.3 Connection State Machine
| From | To | Trigger |
|---|---|---|
| DISCONNECTED | AWAITING_HANDSHAKE | TCP connect or accept |
| AWAITING_HANDSHAKE | CONNECTED | Valid handshake within 10,000 ms |
| AWAITING_HANDSHAKE | DISCONNECTED | Timeout, invalid frame, or duplicate nodeId |
| CONNECTED | DISCONNECTED | Heartbeat timeout, TCP close, consent-withdraw, or error |
Implementations MUST NOT process cognitive frames (state-sync, memory-share, mood, xmesh-insight) in the AWAITING_HANDSHAKE state.
5.4 Heartbeat
Nodes MUST send a ping frame to each peer if no frame has been received from that peer within the heartbeat interval (default: 5,000 ms). Upon receiving ping, a node MUST respond with pong. If no frame is received from a peer within the heartbeat timeout (default: 15,000 ms), the connection MUST be closed.
5.5 Connection Loss
When a transport connection closes unexpectedly (TCP reset, timeout, OS-level close), the node MUST remove the peer from its coupling engine, discard any buffered frames for that peer, and emit a peer-left event. The node SHOULD attempt re-discovery via DNS-SD. Unexpected disconnection MUST be treated as equivalent to the peer becoming unreachable — not as a protocol error.
5.6 Peer Gossip
After handshake, nodes SHOULD exchange peer-info frames containing known peer metadata (nodeId, name, wake channels, last-seen timestamps). This enables transitive peer discovery — a node that has never been online simultaneously with a sleeping peer can learn its wake channel through gossip from a relay node.
5.7 Wake
Nodes MAY register a wake channel (APNs, FCM, or other push mechanism) via the wake-channel frame. Peers MAY use this channel to wake a sleeping node when they have a signal to deliver. Wake requests SHOULD be rate-limited (default cooldown: 300,000 ms per peer).