Celld Fleet Communication Security Model: A Technical Deep Dive

Celld operates on a Zero-Trust, mutual-authentication security model built on Ed25519 identity keys, Noise XK handshake, and AEAD encryption with token-based revocation.

This article explains how the denoland/celld distributed system protects inter-node communication. The security architecture ensures that every fleet member cryptographically proves its identity before exchanging data, with no implicit trust granted based on network location.

Zero-Trust Foundations

Celld rejects perimeter-based security. Every cell—the fundamental unit in a celld fleet—mustauthenticate itself to every peer it contacts. The model rests on three pillars: persistent identity, short-lived authorization, and session-based encryption.

The threat model assumes compromised nodes, passive eavesdroppers, and active man-in-the-middle attackers. Countermeasures are enforced at the protocol layer, making them independent of transport mechanism.

Identity: Ed25519 Long-Term Keys

Each cell generates a persistent Ed25519 key pair during initialization. These keys serve as the cell's identity throughout its lifecycle.

In crates/celld/peer_auth.rs, the PeerAuth struct loads and manages these keys:

// crates/celld/peer_auth.rs
pub struct PeerAuth {
    key_pair: Ed25519KeyPair,
    public_key: PublicKey,
}

The private key never leaves the node. The public key is registered with the fleet leader to establish initial trust.

Key Generation Example

// Deno/TypeScript using the Web Crypto API
const keyPair = await crypto.subtle.generateKey(
  { name: "ED25519", namedCurve: "ED25519" },
  true,
  ["sign", "verify"],
);
await Deno.writeTextFile("node_private.key", 
  await crypto.subtle.exportKey("pkcs8", keyPair.privateKey));
await Deno.writeTextFile("node_public.key", 
  await crypto.subtle.exportKey("spki", keyPair.publicKey));

Authorization: Leader-Signed Peer Tokens

Authorization in celld's security model uses time-bound, cryptographically signed tokens. When a node joins the fleet, the leader inspects its public key and issues a PeerToken if the node is permitted access.

The token structure is defined in crates/celld/peer.rs:

// crates/celld/peer.rs
pub struct PeerToken {
    pub payload: PeerTokenPayload,
    pub signature: Signature,
}

pub struct PeerTokenPayload {
    pub pub_key: PublicKey,
    pub expires: SystemTime,
}

Token creation in peer_auth.rs binds the peer's public key to an expiration timestamp:

// crates/celld/peer_auth.rs (simplified)
pub fn sign_peer_token(
    priv_key: &Ed25519KeyPair,
    peer_pub: &PublicKey,
    expires: SystemTime,
) -> PeerToken {
    let payload = PeerTokenPayload {
        pub_key: peer_pub.clone(),
        expires,
    };
    let sig = priv_key.sign(&bincode::serialize(&payload).unwrap());
    PeerToken { payload, signature: sig }
}

The leader's signature proves token authenticity. The expiration limits the window of compromise if a token is exfiltrated.

Key Exchange: Noise XK Handshake

Celld implements the Noise XK pattern for authenticated key exchange. This pattern provides:

  • Mutual authentication: Both peers verify each other's identity
  • Forward secrecy: Session keys cannot be recovered from long-term keys
  • Noise properties: No unauthenticated cleartext, identity hiding for the initiator

The handshake executes in crates/logic/peer.rs:

// crates/logic/peer.rs (excerpt)
let mut handshake = NoiseHandshake::new_xk(
    &local_static_key,
    &remote_static_key, // from PeerToken
    &mut rng,
);
let msg1 = handshake.write_message(&[])?; // first handshake message
socket.send(msg1).await?;
let msg2 = socket.recv().await?;
handshake.read_message(&msg2)?;
let (cipher, _handshake_state) = handshake.into_transport_mode()?;

XK pattern specifics:

  • X: Static key transmitted to initiator (leader knows peer's key from token)
  • K: Responder knows initiator's static key in advance

After into_transport_mode(), all communication uses the derived session keys.

Encryption: ChaCha20-Poly1305 AEAD

Transport-layer security in celld uses ChaCha20-Poly1305 AEAD for every frame. This provides:

  • Confidentiality: 256-bit symmetric encryption
  • Integrity: 128-bit authentication tag
  • Replay protection: Sequential nonces derived from session state

The implementation lives in crates/celld/protocol.rs. Each encrypted frame includes a nonce, ciphertext, and authentication tag. The nonce space is partitioned to prevent collisions between sender and receiver.

Revocation and Lifecycle Management

Fleet-wide security policies are enforced through the ownership store. The leader maintains the ownership_store.rs registry of authorized peers:

// crates/celld/ownership_store.rs (simplified)
pub fn is_authorised(&self, peer_id: &PeerId) -> bool {
    self.active_peers.contains_key(peer_id)
}

pub fn revoke(&mut self, peer_id: &PeerId) {
    self.active_peers.remove(peer_id);
}

When revoke() removes a peer, all active connections to that peer are terminated. The cryptographic binding of session keys to the revoked identity prevents reconnection without a fresh, valid token.

Transport Agnosticism

The celld security model operates over any reliable byte stream. The same handshake and encryption layer functions identically across:

  • WebSockets
  • HTTP/2 streams
  • Raw TCP connections

This is enabled by the abstraction in crates/celld/runtime.rs, which wires the security layer into the chosen transport without protocol modification.

Security Guarantees Summary

Property Mechanism Source File
Identity authenticity Ed25519 signatures peer_auth.rs
Authorization validity Leader-signed PeerToken with expiration peer.rs
Session key establishment Noise XK handshake (Curve25519 ECDH) logic/peer.rs
Data confidentiality ChaCha20-Poly1305 AEAD protocol.rs
Peer eviction ownership_store removal + connection drop ownership_store.rs

Summary

  • Zero-Trust architecture: No implicit trust based on network position; every peer cryptographically authenticates
  • Ed25519 identity keys: Long-term, persistent node identifiers stored in peer_auth.rs
  • Token-based authorization: Leader-issued, time-bound PeerToken grants fleet membership
  • Noise XK handshake: Mutual authentication with forward secrecy via Curve25519 ECDH
  • AEAD encryption: ChaCha20-Poly1305 protects all transported data with integrity and replay resistance
  • Immediate revocation: Leader-controlled ownership_store enables instant peer removal

Frequently Asked Questions

How does celld prevent a compromised node from impersonating others?

Each node's identity is tied to its private Ed25519 key, which never leaves the node. The Noise XK handshake requires proof of possession of this key. A compromised node can only impersonate itself—it cannot forge signatures for other nodes' public keys. The protocol binding in logic/peer.rs cryptographically locks sessions to the proven identity.

What happens when a node fails to present a valid peer token?

The Noise handshake aborts before any application data flows. The initiator sends its static key and token signature in the first message; the responder verifies the leader's signature on the token and checks expiration. If verification fails, the handshake state machine in logic/peer.rs returns an error and the underlying connection closes.

Can the leader read all fleet communication?

No. The leader signs authorization tokens but does not possess session keys. The Noise XK handshake derives shared secrets directly between communicating peers using ephemeral Curve25519 key exchanges. The leader's role is limited to identity verification and access control, not traffic decryption.

How does celld handle token expiration during long-lived connections?

Session keys outlive tokens but new connections require fresh tokens. The AEAD session established by the Noise handshake remains valid indefinitely for that specific connection pair. However, if the connection drops and reconnection is attempted, the peer must present a current, unexpired token. This balances operational continuity with revocation granularity.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →