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:

KeyRequiredValue
node-idMUSTNode UUID
node-nameMUSTHuman-readable name
hostnameSHOULDMachine 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 version field 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 extensions field SHOULD list supported protocol extensions (e.g., ["consent-v0.1"]). Nodes MUST ignore unrecognised extensions.
  • The inbound node MUST wait for a handshake frame 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

DISCONNECTED
initial state
TCP connect / accept
AWAITING_HANDSHAKE
10s timeout
valid handshake received
CONNECTED
peer registered, frames routed
timeout / close / consent-withdraw
DISCONNECTED
peer removed, re-discover
FromToTrigger
DISCONNECTEDAWAITING_HANDSHAKETCP connect or accept
AWAITING_HANDSHAKECONNECTEDValid handshake within 10,000 ms
AWAITING_HANDSHAKEDISCONNECTEDTimeout, invalid frame, or duplicate nodeId
CONNECTEDDISCONNECTEDHeartbeat 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).