Mesh Architecture

graph LR
  C[Client / CLI / Desktop] -->|gRPC :50052| N1[Node A]
  C -->|gRPC :50052| N2[Node B]
  N1 <-->|Synapse QUIC :50053| N2
  N1 --> DB1[(control.db)]
  N2 --> DB2[(control.db)]

The two planes

A node listens on two independent surfaces. Keeping them separate is what lets a mesh upgrade one plane without dropping sessions on the other.

PlaneDefault listenProtocolCarries
Client--grpc, default :50052gRPCsession streaming, attach/detach, session listing, transfer control
Peer--synapse-listen, default :50053Synapse QUIC/UDP, protobuf framing; WebSocket/TCP retained for rolling-upgrade compatibilitynode-to-node session movement and heartbeats

Because :50052 is the client plane, it is also what sonny --mesh-self exposes when you embed a node inside a chat.

The bind address is rarely the address a peer can reach. --grpc says where to listen; --advertise-addr says what to publish to peers — for example a Tailscale DNS name such as lothiriel:9052. On a single machine you can ignore it. On a multi-host mesh it is required, because publishing :50052 to a peer is publishing an address that peer cannot route to.

Identity, transport security and authorization

  • --cert, --key, --ca configure TLS on the gRPC plane and mTLS on the peer plane.
  • --trust-domain sets the SPIFFE trust domain used for peer SVIDs (default mesh.local), so peers authenticate as workload identities rather than as hosts.
  • --authz-policy points at the policy that decides which caller may do what. Treat the client plane as an authenticated surface: a node that will host sessions should not expose :50052 to a network it does not control.

Control-plane database

Each node keeps a SQLite control-plane database, --control-db, defaulting to <data>/control.db. It is where session and node bookkeeping lives, which is why sonny mesh sessions can answer questions about sessions that finished a while ago and why --all can include completed ones.

Capability advertisement

A node announces what it can actually run, so routing decisions are made against facts rather than guesswork:

FlagAdvertises
--providersLLM providers this node can reach
--modelsmodels this node can serve
--packsrecipes installed on this node
--region, --zoneplacement hints for routing
--label key=valuearbitrary selector labels (repeatable)
--max-sessionsconcurrency ceiling for the node

Isolation reporting

Every session carries the isolation it actually achieved — backend, assurance level, and verification state. These are not configuration echoes; they are the achieved values, and sonny mesh sessions filters on them so you can prove which sessions ran where you required:

# Sessions whose isolation was not verified
sonny mesh sessions --isolation-state unverified

# Sessions on a specific backend, sorted by assurance
sonny mesh sessions --isolation-backend docker --sort assurance

See Working with sessions for the full filter set.

Coordinator

To attach a fresh node to a mesh that already exists, pass --coordinator with the address of a node already in it. Without a coordinator, the node bootstraps its own mesh and other nodes join it through --mesh-peer.