Handshake
Handshake: the net-kernel greeting + authentication decision.
When two nodes connect, each sends a Hello { its identity, a fresh nonce }. Each then replies with a proof = ClusterAuth.prove(secret, peer_nonce). A node accepts the peer iff the peer's proof matches the nonce it issued — so only holders of the cluster secret join, and a proof can't be replayed for a different nonce. This module is the pure protocol logic; the socket I/O (framing via NetFrame, send/recv) lives in the net-kernel on top.
Types
role names the connection this hello opens: "control" (SWIM, registry sync, monitors, CREDIT, DELIVERY_FAILED) or "data" (ACTOR_MSG, RPC). One of each per peer since the control/data split (specs/progress/2026-09-14-distributed-plane-flow-control-and-control-channel.md, step 2). A hello without the field -- every node before the split -- is a control connection, so the two wire shapes interoperate.
addr is the sender's ADVERTISED listen address ("host:port"), so an acceptor learns where to dial the peer back before any gossip arrives (the cluster node service). "" when not sent: a hello without it (every node before the service) decodes as before.
cert is present only in CERTIFICATE mode (ClusterAuth.Certified): the sender's signed certificate (NodeCert.to_bytes) and a fresh ephemeral X25519 public key for the connection's MAC key agreement. It is sent as a seventh element, ["cert1", Bin(cert), Bin(eph)]; a shared-secret hello never has it and is byte-identical to a pre-11a node's.
Functions
Shared-secret mode's decision. Given the cluster [secret], the nonce [our_nonce] we issued to the peer, the peer's decoded hello [peer], and the peer's proof [peer_proof]: Ok with the identity the hello claims when the proof is ClusterAuth.prove(secret, our_nonce), else Err("Handshake: peer failed authentication"). A proof made for another nonce never verifies.
The two modes do not mix: a certificate-mode node refuses a shared-secret peer and a shared-secret node refuses a certificate-mode one, each naming what the other side must be given. (A pre-11a node cannot decode a certificate hello at all and fails with "malformed hello".) Ok(()) when the modes agree.
Decode the bytes of encode_hello, and the older shapes: a 6-element hello (no certificate), a 5-element one (no address: addr ""), and a
Err
for bytes that are not MessagePack, or "Handshake: malformed hello".
Serialize [h] to MessagePack bytes: [name, node_id, incarnation, nonce, role], then [addr] when it is not "" (so a hello without an address is byte-identical to the pre-service shape). A certificate hello always carries the address (possibly "") and then ["cert1", Bin(cert), Bin(eph)].
A shared-secret hello from [identity] with challenge [nonce], opening a control connection and advertising no address.
As make_hello_role, advertising [addr] (\
A certificate-mode hello: as make_hello_addr, carrying this node's signed certificate [cert] (NodeCert.to_bytes) and the connection's ephemeral X25519 public key [eph].
As make_hello, opening a connection of [role] (role_control() or role_data()).
The role of a control connection (SWIM, registry sync, monitors, CREDIT, DELIVERY_FAILED): \
The role of a data connection (ACTOR_MSG, RPC): \
Check the certificate a certificate-mode peer sent at [now] (unix seconds): signed by the operator key, not expired, not revoked, and naming the node the hello claims to be -- its name is the certificate URI's last segment, and its node id is that name's (NodeIdentity.name_id). Returns the certificate and the node's public key, or a reason.