Skip to content

Architecture

Process Model#

xmppd uses a Postfix-inspired multi-process architecture. The master supervisor spawns and monitors isolated child processes, each handling a specific responsibility.

xmppd (master, runs as root)
├── xmppd-core     (C2S, runs as jabber)     ← N worker threads
├── xmppd-auth     (authentication, runs as jabber)
└── xmppd-s2s      (federation, runs as jabber)

Privilege separation: The master binds privileged ports (5222, 5269) while running as root, then passes the file descriptors to child processes via --listen-fd. Children drop privileges to the configured user immediately after initialization.

Fault isolation: If xmppd-auth crashes, the master restarts it with exponential backoff. Client connections in xmppd-core are unaffected during the brief restart window (auth requests queue in the IPC buffer).

Thread-Per-Core#

xmppd-core uses a thread-per-core model for horizontal scaling within a single process.

xmppd-core process
├── Worker 0 — kqueue + listener fd 5 + sessions 0..1023
├── Worker 1 — kqueue + listener fd 6 + sessions 0..1023
├── Worker 2 — kqueue + listener fd 7 + sessions 0..1023
└── Worker 3 — kqueue + listener fd 8 + sessions 0..1023

Each worker thread has:

  • Its own kqueue event loop (no shared epoll/kqueue)
  • Its own SO_REUSEPORT_LB listener (kernel distributes connections)
  • Its own session slots (no cross-thread session access)
  • Access to a shared SessionMap (JID-keyed, RwLock-protected)

Cross-thread message delivery uses MPSC ring buffers with pipe-based wakeup. When Worker 0 needs to deliver a message to a session on Worker 2, it enqueues the stanza into Worker 2's MPSC buffer and writes a byte to Worker 2's wake pipe.

IPC Protocol#

Communication between processes uses a binary IPC protocol over Unix domain sockets. Messages are length-prefixed (4 bytes LE + 1 byte tag + fields).

Tag Direction Purpose
0x01 core → auth Authentication request (username, mechanism, client IP)
0x02 auth → core SASL challenge
0x03 auth → core Authentication success
0x04 auth → core Authentication failure
0x05 core → auth SASL response
0x0C auth → core Mechanism list (sent on IPC connect)
0x10 core → s2s Outbound stanza delivery
0x11 s2s → core Inbound stanza delivery

Storage#

xmppd uses a comptime-generic storage subsystem. The StorageBackend trait defines the interface, and multiple backends are available at build time:

Backend Use Case Build Flag
LMDB Operational data (default) -Dop-storage=lmdb
RocksDB Message archive (MAM) (always used for archive)
SQLite Lightweight alternative -Dop-storage=sqlite
Memory Testing only (unit tests)

Operational stores (LMDB by default):

  • UserStore — SCRAM credentials (salt, stored key, server key, iterations)
  • RosterStore — Contact list with subscription state machine
  • VCardStore — Raw vCard XML blobs
  • RoomStore — MUC room configuration and affiliations
  • OfflineStore — Offline message queue with per-user cap
  • LockStore — Permanent account locks
  • InviteStore — Registration invitation codes

Archive store (RocksDB):

  • ArchiveStore — XEP-0313 MAM with paginated query and retention policy

MUC Architecture#

Multi-User Chat is handled inline in xmppd-core (not a separate process). Rooms are sharded across worker threads using hash(room_jid) % N_workers.

Each worker maintains:

  • Owned rooms — full room state (occupants, config, mailbox)
  • Shadow rooms — lightweight mirrors of rooms owned by other workers

Groupchat fan-out uses worker-level multicast: instead of N MPSC enqueues (one per remote occupant), xmppd sends one multicast message per remote worker. This makes fan-out O(workers) instead of O(occupants).

Connection Lifecycle#

1. TCP accept (SO_REUSEPORT_LB assigns to a worker)
2. XML stream open
3. STARTTLS negotiation (OpenSSL)
4. SASL authentication (via IPC to xmppd-auth)
5. Resource binding
6. Session established — stanza routing begins
7. Stream Management enabled (XEP-0198)
8. ...normal XMPP operation...
9. Stream close (or abnormal disconnect → SM resume window)