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
public-keyMUSTEd25519 public key (base64url, RFC 4648 Section 5)
hostnameSHOULDMachine hostname
groupMAYMesh group identifier (Section 5.8). Default "default" if absent.

To prevent duplicate connections, the node with the lexicographically smaller nodeId MUST initiate the outbound TCP connection. The other node MUST NOT initiate.

Relay-based discovery. On platforms where mDNS is unavailable (cloud VMs, Windows without Bonjour SDK), nodes SHOULD use the relay’s relay-peers response as the discovery mechanism. Implementations SHOULD support both: DNS-SD for LAN, relay-peers for WAN.

5.2 Handshake

Upon connection, both sides MUST exchange the following frames in order:

1. handshake    { type: "handshake", nodeId: "<uuid>", name: "<name>",
                  publicKey: "<base64url>", version: "1.1.0", extensions: [],
                  lifecycleRole: "participant",               [sender MUST; absent → "participant"]
                  group: "<group-id>" }                       [optional, default "default"]
2. peer-info    { type: "peer-info", peers: [...] }           [if known]
3. wake-channel { type: "wake-channel", platform, token, env } [if configured]

Deprecated — state-sync. Earlier revisions exchanged a state-sync frame carrying the node’s hidden-state vectors (h₁, h₂) at this point in the handshake. 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; peer influence is mediated entirely by CMBs evaluated through SVAF (Section 9.2).

  • The version field MUST be the MMP specification version the node implements (e.g., "1.1.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., ["mesh-group-v0.1"]). Nodes MUST ignore unrecognised extensions.
  • The group field is OPTIONAL and identifies the mesh group the node wishes to join (Section 5.8). A handshake without group MUST be treated as group = "default". When two nodes handshake and discover that their declared groups differ, the receiver MUST close the connection.
  • 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 via the same transport type, the new connection MUST be closed (duplicate guard). If the existing connection uses a different transport type (e.g. peer connected via relay, new connection via LAN TCP), the new connection MUST be accepted as a secondary transport per Section 4.6.

lifecycleRole. Senders MUST include a lifecycleRole field with value participant (default), validator, or anchor; a receiver MUST treat a handshake without it as participant (backward compatibility with older nodes). The declared role is a discovery hint only — authority to apply validator-origin anchor weight (Section 6.4) or identify feedback CMBs (Section 11) is resolved through the signed role-grant chain (§6.5–§6.6), never from the handshake.

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
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, or error

Implementations MUST NOT process cognitive frames (cmb, 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 and Transport Failover

When a transport connection closes unexpectedly (TCP reset, timeout, OS-level close), the node MUST check whether other transports for the same peer are still active (see Section 4.6 Multi-Transport Per Peer).

  • If other transports remain healthy: the node MUST switch sending to the next highest-priority transport. The peer MUST NOT be removed. No peer-left event is emitted. The node SHOULD log the transport switch.
  • If no transports remain: the node MUST remove the peer from its coupling engine, discard buffered frames, and emit a peer-left event. The node SHOULD attempt re-discovery via DNS-SD.

Unexpected disconnection of a single transport MUST be treated as a transport-level event, not a peer-level event. The peer is only unreachable when all transport paths are exhausted.

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).

5.8 Mesh Groups

A SYM node MAY declare membership in a mesh group at handshake time via the optional group field (Section 5.2). A mesh group is a named cohort of nodes that exchange application-layer frames only with each other. Mesh groups give an operator a way to host multiple mutually-isolated meshes on the same relay or LAN segment without per-agent application changes, and they give an application a way to constrain its peers to instances of itself rather than every node on the wire.

Group identifier syntax. A group identifier is a string of [a-z0-9-_.]+, max 64 characters, case-sensitive. The literal string "default" is reserved as the implicit group of every node that does not declare a group; this preserves backward compatibility with nodes that predate this section.

Protocol guarantee. A node in group G_A MUST NOT exchange any application-layer MMP frames (handshake fields beyond identity and version, cmb, mood, peer-info, xmesh-insight) with a node in group G_B when G_A ≠ G_B. Transport-layer connection establishment and ping/pong heartbeats are out of scope and MAY remain active across groups.

Layer placement. A mesh group is a Layer 2 (Connection) concept. The application layer SHOULD declare its group at SDK initialisation. The relay (Section 4.4) is a dumb pipe — relay-auth carries no group field and the relay MUST NOT inspect payloads (Section 4.4.4) — so it need not (and cannot) enforce group isolation. Isolation is enforced at the endpoints: the authoring group is bound into the signed CMB (§18.3.1 audience binding), and a receiver MUST reject a frame whose group does not match its own connection’s group, even if a relay misdelivers it. A relay MAY additionally scope rooms by a group-derived key as defense in depth. Nodes participating in LAN Bonjour discovery SHOULD enforce group isolation by checking the peer’s declared group at handshake and closing the connection on mismatch. The connection-level error frame for a group mismatch is described in Section 7.2.

Recommended naming convention (non-normative). The protocol does not parse group identifiers beyond the character set and length checks above. Operators of meshes with more than a handful of groups SHOULD adopt a hierarchical dotted-path convention <app>[.<environment>][.<cohort>], e.g. acme.prod, acme.dev, assistants.default, research.lab. The dots are convention only; tooling MAY use them for prefix-based grouping but the protocol does not require this.

SVAF and group filtering. SVAF (Layer 4, Section 9) per-field evaluation runs after group filtering: cmb frames from peers in different groups never reach the SVAF evaluator.

The naming convention above is the complete normative surface; deeper design rationale is runtime documentation, not part of this specification.

§5.9–5.11 — Informative

Sections 5.9–5.11 describe an informative design pattern for composing meshes — not a normative wire. The single-mesh protocol (§1–§5.8, §6–§20) is complete and unaffected without it. There is one reference implementation (an xMesh gateway prototype) and it realizes only a subset of the pattern (see “Reference implementation” below); the core runtime is single-group. Adopt this as a topology pattern with a stated production-security bar, not as a shipped protocol feature.

5.9 Interior and Boundary (the composition idea)

A mesh presents itself to another mesh as a single node: a gateway. A gateway participates in its own interior group as an ordinary node (§5.2, single group on the wire) and presents a boundary to exterior gateways over a separate transport. It is not one handshake declaring several groups — interior participation and the exterior boundary are distinct connections.

Admit-then-reproject, not relay (the intended grammar). The pattern forbids forwarding an interior frame outward. Instead a gateway admits its interior cognition through SVAF (§9) and emits a new lossy CAT7 projection (§2.7) outward — its own cognition, signed, carrying its own lineage (§15), never a relayed copy. This preserves the §5.8 guarantee (no interior frame crosses a group boundary) by construction. Implementation status: the reference prototype does not yet do this — see below.

5.10 The Gateway Node

A node is a membrane over an arbitrary interior. What makes something a node is entirely its boundary behavior: a stable identity (Section 3), a CAT7 projection of its state (Section 8), and a sovereign SVAF admission of others’ projections (Section 9). The interior behind the membrane is unconstrained. Two node types share this one membrane:

Atom nodeGateway node
interiorone agent (mind + store + SVAF)a sub-mesh (many nodes)
identityits ownits own (it represents the interior)
projectsa lossy view of its private statea lossy view of its interior’s aggregate cognition
admitsinto its own storeinto a boundary policy; MAY re-project inward
membershipone groupone interior group + an exterior boundary (Section 5.9)

A gateway node is an ordinary node whose interior happens to be a mesh; its domain lens (Section 3.1) is “represent my interior.” Because the same emit / admit grammar holds at every scale — agent, team, org, cross-org — the mesh is fractal: any mesh MAY appear as a single node inside a larger mesh.

Relation to Section 3.2. “One agent, one node” is preserved. A gateway is not a shared identity: it has its own nodeId, its own keypair, and its own SVAF. Its interior agents are separate nodes on a separate (interior) group; the gateway participates in that interior group as an ordinary node and presents an exterior boundary (§5.9) — evaluating each side through its own lens, the very property Section 3.2 protects.

No center, per level. The “no center” invariant (Section 2.3) is enforced at each boundary, not as a claim about interiors. A gateway’s interior MAY be organized however it likes — centered or not; that choice does not leak, because only the gateway’s projection crosses. A member of an outer mesh MAY therefore be a gateway over a centered interior while the outer mesh remains center-free. Federation couples meshes; it MUST NOT synchronize them.

5.11 Boundary Behavior

For two gateway nodes A and B, each a mesh’s membrane, all cross-mesh behavior is the existing grammar applied at the edge:

  • Discovery. Cross-mesh discovery is by invitation or registry, not mDNS (Section 5.1 is LAN-only). A knows B as one node-id at one address; B is a peer, never a visible population.
  • Projection. What crosses is B’s own emissions — its lossy CAT7 projection of what it admitted internally — never B’s raw interior CMBs.
  • Membrane lineage. A gateway’s outward emission is a boundary root: its lineage (Section 15) MUST NOT carry the content-addresses of interior CMBs. An outer node citing it traces to the gateway and no further; the interior is opaque past the membrane, as required by hidden-state locality (Section 2.7). A gateway MAY retain the interior-to-boundary mapping privately, so it can re-project admitted outer cognition inward with correct interior lineage; that mapping MUST NOT cross the outward boundary.
  • Faithful projection. A gateway’s outward CAT7 should be a truthful lossy summary of the interior it represents, not a material misrepresentation. Because summarization is lossy, faithfulness is attestable, not bitwise: the intended design has the gateway sign its projection and record, in an admission attestation (§6.5), the interior verdict aggregate it was derived from — so an outer admitter can weigh the boundary claim by earned authority. This attestation is part of the production security bar and is unbuilt in the prototype.
  • Echo control. A cognition admitted from A and projected back toward A should origin-dedup at the boundary so cross-mesh loops do not amplify. Because a boundary root strips interior lineage, the dedup cannot match interior roots; instead a boundary projection carries its own cross-mesh provenance key (a boundary address, not an interior content-address), and a reprojection cites that key — so A detects the loop without the interior ever being exposed. (Unbuilt in the prototype; §15.7.1, on which an earlier draft leaned, is itself unimplemented.)
  • Partition. If B is unreachable, A’s mesh keeps cohering. Each mesh is independently alive; there is no cross-mesh consensus to stall on.

Boundary transport. The boundary is a dumb request/response transport (HTTP in the reference implementation): a gateway POSTs its projection to each configured peer gateway, and the peer ingests it as an opaque cross-mesh observation. The transport carries the projection only; it holds no shared store and performs no admission or routing on behalf of the meshes — a component that grew a shared store or an admission brain would be a center and must not be introduced. Discovery is by configuration (a gateway knows its peers by id + address + credential), not mDNS (§5.1 is LAN-only) and not a registry.

Production security bar

Federation crosses organizational trust boundaries, so a production gateway boundary requires, at minimum: (1) the projection is a signed cmb1- CMB authored by the gateway (§18.3.1), with the from gateway origin-authenticated by that signature — never a self-declared, unsigned field; (2) anti-replay (the signed createdAt plus receiver-side dedup); and (3) a boundary credential scoped to the boundary — never a full control-plane token. A boundary that accepts unsigned projections, trusts a self-declared origin, or authenticates with an admin credential is not safe for cross-org use.

Reference implementation (prototype)

The xMesh reference gateway realizes a subset: it computes a lossy summary of its interior’s cognition and HTTP-POSTs it to configured peers, which ingest it opaquely. It does not yet admit inbound projections through SVAF, reproject them inward, sign or attest its projection, or echo-dedup; its projection is a summary object, not yet a schema-valid CAT7 CMB; and it does not yet meet the production security bar above. Treat it as a prototype of the pattern, not a complete or production implementation.

Sections 5.9–5.11 are informative and change no single-mesh contract: they describe how meshes may compose, drawing on the concepts of Sections 2.3, 2.7, 3.2, and 15. 1.0.6 introduced the composition pattern; a normative cross-mesh wire is not claimed — the reference implementation is a prototype and the production-security bar above is a prerequisite, not a shipped guarantee. Every single-mesh node is unaffected.