XMPP From Zero to Production Book
A Complete Developer's Guide
Build real-time messaging that survives 2G — from your first stanza to a clustered Ejabberd deployment.
Running example throughout: MaasaiChat , a chat app for Maasai communities across rural Tanzania and Kenya — 2G/3G links, low-end Android phones, connections that drop constantly. Every technical decision in this book is shaped by that reality.
First Edition · 2026
Written for backend engineers, mobile developers, and architects who need to own their messaging stack.
No prior XMPP knowledge assumed. By the final chapter you will have deployed a production Ejabberd server, implemented every XEP MaasaiChat needs on Android and iOS, and hardened it for real traffic.
How to use this book
Part 1 teaches the protocol from absolute zero. Read it in order.
Part 2 is a reference — one chapter per XEP, all built on the same six-part template. Read the Core and Messaging chapters, then dip into the rest as you need them.
Part 3 is hands-on Ejabberd. Keep a terminal open.
Part 4 is the mobile SDK guide (Android Smack, iOS XMPPFramework).
Part 5 is production: security, database, scaling, network tuning, pitfalls.
Every XEP chapter answers the same six questions in the same order:
1. What problem does it solve?
2. The XML stanzas, explained line by line
3. A real MaasaiChat example
4. How Ejabberd handles it
5. What the mobile dev implements
6. What the backend dev implements
Every chapter ends with a What you learned box and a checkpoint before the next one.
Conventions: terminal commands are shown in fenced blocks, XML stanzas are complete and copy-pasteable, and the four MaasaiChat users appear in every example:
ole@maasaichat.com Ole Saitoti — elder
naserian@maasaichat.com Naserian — young warrior
enkiama@maasaichat.com Enkiama — village chief
nkeri@maasaichat.com Nkeri — cattle trader
Table of Contents
Part 1 — Foundation
1 What is XMPP?
2 The XMPP Vocabulary — Key Terms & Definitions
3 XML Basics for XMPP
4 The JID Address System
5 The Three Stanzas
6 Namespaces & XEPs Explained
7 The XMPP Stream Lifecycle
Part 2 — XEPs Complete Reference
Core
8 XEP-0030 Service Discovery
9 XEP-0115 Entity Capabilities
10 XEP-0199 XMPP Ping
11 XEP-0198 Stream Management (critical for 2G/3G)
12 XEP-0280 Message Carbons
13 XEP-0313 Message Archive Management (MAM)
Messaging
14 XEP-0085 Chat State Notifications
15 XEP-0184 Message Delivery Receipts
16 XEP-0333 Chat Markers
17 XEP-0308 Last Message Correction
18 XEP-0424 Message Retraction
19 XEP-0444 Message Reactions
20 XEP-0297 Stanza Forwarding
21 XEP-0461 Message Replies
22 XEP-0359 Stable & Unique Stanza IDs
23 XEP-0334 Message Processing Hints
Group Chat
24 XEP-0045 Multi-User Chat (MUC)
25 XEP-0249 Direct MUC Invitations
26 XEP-0317 Hats
27 XEP-0425 Message Moderation
28 XEP-0490 Message Displayed Synchronization
File & Media
29 XEP-0363 HTTP File Upload (the one you'll actually use)
30 XEP-0065 SOCKS5 Bytestreams
31 XEP-0234 Jingle File Transfer
32 XEP-0264 Jingle Content Thumbnails
Calls
33 XEP-0166 Jingle
34 XEP-0167 Jingle RTP Sessions
35 XEP-0176 Jingle ICE-UDP Transport
36 XEP-0177 Jingle Raw UDP Transport
37 XEP-0215 External Service Discovery (TURN/STUN credentials)
38 XEP-0320 DTLS-SRTP in Jingle
Push & Notifications
39 XEP-0357 Push Notifications (FCM on Android)
Security & Encryption
40 XEP-0384 OMEMO Encryption
41 XEP-0388 Extensible SASL Profile
42 XEP-0440 SASL Channel Binding
Presence & Roster
43 XEP-0054 vcard-temp
44 XEP-0153 vCard-Based Avatars
45 XEP-0292 vCard4 Over XMPP
46 XEP-0083 Nested Roster Groups
47 XEP-0144 Roster Item Exchange
PubSub
48 XEP-0060 Publish-Subscribe
49 XEP-0163 Personal Eventing Protocol (PEP)
History & Archive
50 XEP-0059 Result Set Management
51 XEP-0313 MAM in depth (querying, paging)
52 XEP-0430 Inbox
Enterprise
53 XEP-0050 Ad-Hoc Commands
54 XEP-0004 Data Forms
55 XEP-0055 Jabber Search
56 XEP-0077 In-Band Registration
57 XEP-0133 Service Administration
Federation
58 XEP-0220 Server Dialback
59 XEP-0288 Bidirectional Server-to-Server
Part 3 — Ejabberd in Practice
60 Architecture & the Erlang/BEAM Foundation
61 Docker Setup, Step by Step
62 ejabberd.yml — The Complete Guide
63 ejabberdctl — Every Command You'll Use
64 REST API — Complete Reference
65 OAuth Authentication
66 The Auth Bridge (HTTP auth → your backend as gatekeeper)
67 Clustering Two Nodes
68 MUC Administration
69 Monitoring & Logging
Part 4 — Mobile SDK Guide
70 Android with Smack — connect, auth, send/receive, all XEPs, custom stanzas
71 iOS with XMPPFramework — same coverage
Part 5 — Production
72 Security Hardening
73 PostgreSQL Backend
74 Scaling Path
75 East African Network Optimization
76 Common Pitfalls
77 Monitoring Setup
Part 1 — Foundation
Chapter 1 — What is XMPP?
1.1 The one-sentence answer
XMPP is an open protocol for sending small pieces of XML from one address to another, in real time, over a long-lived connection.
That's it. Everything else in this book is detail on top of that sentence. Each word was chosen deliberately:
Open protocol — nobody owns it. It's an IETF standard (RFC 6120 for the core, RFC 6121 for instant messaging and presence). You don't ask permission or pay a license. Contrast this with WhatsApp's protocol, which is closed — you cannot legally build a server that speaks it.
Small pieces of XML — the unit of communication is a stanza , a short XML fragment. A single chat message is a few hundred bytes.
From one address to another — every user and service has an address called a JID (Jabber ID). Routing is built into the protocol.
In real time — messages arrive the instant they're sent, not when the client next polls.
Over a long-lived connection — the phone opens one TCP connection to the server and keeps it open for hours. Messages flow both ways over that single pipe.
XMPP originally stood for eXtensible Messaging and Presence Protocol . It started in 1999 under the name Jabber , created by Jeremie Miller. You'll still see "Jabber" everywhere — in the JID name, in library names, in old docs. Treat "Jabber" and "XMPP" as the same thing.
1.2 Why a long-lived connection matters (and why HTTP doesn't fit)
To understand XMPP, understand the problem it was built to avoid.
The web runs on HTTP, which is request/response . The client asks, the server answers, the connection closes. Perfect for loading a web page. Terrible for chat, because chat is server-initiated : the server needs to push a message to you the moment someone sends it, and it has no idea when that will be.
There are three ways to force chat onto HTTP, and all three are bad on a rural network:
Approach How it works Problem on 2G/3G
--------------------------------------------------------------------------
Short polling Ask "any messages?" every 3s Wastes battery + data,
message lag up to 3s
Long polling Ask, server holds request open Constant reconnects,
until a message arrives, repeat each carries TCP+TLS cost
WebSocket over One socket, but you build the You reinvent routing,
raw HTTP entire chat protocol yourself presence, offline, MUC...
XMPP solves this at the protocol level. The phone opens one connection, authenticates once , then both sides send stanzas whenever they want. No repeated handshakes. No polling. On a 2G link where a new TCP+TLS handshake costs several round-trips over 300–800 ms latency, "connect once and stay connected" is the difference between a usable app and an unusable one.
MaasaiChat phone maasaichat.com server
(Naserian's Tecno) (Ejabberd)
| |
|------ open TCP connection -------------->|
|<----- keep it open, both directions -----|
| |
| message to ole@maasaichat.com ------->| routes it
| |
|<------ message from enkiama@... ---------| pushed instantly
| |
| (connection stays open |
| for hours) |
One pipe. Messages flow both directions. That's the core idea.
1.3 What actually travels down the pipe
Once connected, the phone and server exchange stanzas . There are exactly three kinds (Chapter 4 goes deep). For now, just see them.
A chat message from Naserian to the elder Ole:
Elder, the cattle are safe at the river.
Naserian telling the server she is online:
chat
Herding near Ngorongoro
Naserian asking the server a question and expecting an answer:
Three stanza types — message , presence , iq — and everything XMPP does is one of those three, possibly with extra XML tucked inside. That extra XML is what a XEP is, and it's why XMPP can grow reactions, calls, and file upload without ever changing the core (Chapter 5).
1.4 XMPP is the rulebook, Ejabberd is the builder
Here is the single most common confusion for newcomers, and clearing it up now makes the rest of the book effortless: XMPP and Ejabberd are not the same thing, and one is not "inside" the other.
XMPP is a specification — a set of documents written by the XMPP Standards Foundation. It is pure guidance. It says things like "a chat message must have a
element," "addresses look like user@domain/resource ," "a typing indicator uses this namespace." That's all it is. Rules on paper. The XMPP spec, by itself, cannot route a single message — in the same way a rulebook cannot play the game.
Ejabberd is a builder that read those rules and wrote the code. The team at ProcessOne read every relevant document and implemented it in Erlang, producing an actual running server that obeys the rules. Ejabberd is the thing that accepts connections, routes stanzas, and stores offline messages.
The cleanest way to hold this:
Building code (the rules) The builder (follows the rules)
------------------------- ------------------------------
"walls must be 30cm thick" --> reads the code,
"doors must be 2m high" actually builds the house,
"foundation must be concrete" following every instruction
The building code is just paper. The house is real and you
It builds nothing by itself. can live in it.
XMPP = the building code Ejabberd = the builder
Developers already know this pattern from tools they use every day:
The language / rules The thing that speaks it
-------------------- ------------------------
HTTP --> Nginx, Apache, Tomcat
SQL --> PostgreSQL, MySQL
XMPP --> Ejabberd, Prosody, Openfire
Nobody says "HTTP is inside Nginx." HTTP is the rulebook; Nginx is a program that follows it. XMPP and Ejabberd relate the exact same way.
The chain: who writes the rules, who builds
Because XMPP is open guidance, many different teams read the same documents and each build their own piece — and because they all follow the same rules, all the pieces interoperate:
XMPP Standards Foundation writes the guidance (the XEPs)
│ "delivery receipts work like THIS"
▼
ProcessOne reads it, writes Erlang → Ejabberd (server)
Gajim team reads it, writes Python → Gajim (desktop client)
Smack team reads it, writes Java → Smack (Android library)
sendxmpp author reads it, writes Perl → sendxmpp (CLI tool)
Different code. Same guidance. They all understand each other. ✅
This is why the Gajim desktop app on Ole's laptop, the Smack-powered MaasaiChat app on Naserian's Tecno, and a sendxmpp script on a server can all exchange messages through Ejabberd without anyone coordinating — they are all following the same rulebook.
And it's the reason you write almost no protocol code yourself. Ejabberd is already built. Smack is already built. You read the XEPs to understand , then use those implementations, and only write custom code for MaasaiChat's own extensions (its own namespaces for things the standard doesn't cover). The 25-year-old rulebook, and the battle-tested builders who followed it, do the rest.
You (MaasaiChat) read XEPs to understand the rules
use Ejabberd (server, already built)
use Smack (Android, already built)
write custom code ONLY for your own
app-specific extensions ✅
Catch it in one line: XMPP is the language, Ejabberd speaks it, Gajim and Smack also speak it — everyone understands each other because everyone follows the same rulebook.
1.5 Who uses XMPP today
XMPP is not a museum piece. It quietly runs a large chunk of the messaging world:
WhatsApp was built on a heavily modified fork of Ejabberd — the exact server this book deploys. The protocol you're learning is the ancestor of the app two billion people use.
Nintendo Switch uses XMPP/Ejabberd for online chat and presence.
Zoom acquired the XMPP-based team-chat product now sold as Zoom Team Chat.
Google Talk was XMPP for its entire life and even federated with outside XMPP servers.
Jitsi, HCL Sametime, and many carrier, ISP, and government messaging systems run on XMPP today.
The common thread: when an organization needs self-hosted, standards-based, massively scalable real-time messaging that they fully control, XMPP keeps being the answer. That is exactly MaasaiChat's position — you cannot depend on someone else's servers for a community tool in rural Tanzania and Kenya, so you run your own.
1.6 The messaging landscape — XMPP and its alternatives
XMPP is not the only way to build a chat app. Before committing, you should know what else exists, who runs on it, and the honest trade-offs. There are six real families of choice.
1. XMPP (this book)
Open IETF standard. Servers: Ejabberd (what we use), Prosody, Openfire. Clients everywhere.
Pros Cons
--------------------------------------------------------------
Open standard, no vendor lock-in Learning curve — it's a real
Self-hosted, you own the data protocol with real depth
Routing, presence, offline, MUC, XML is verbose vs binary
archive, push are built in (mitigated by compression)
Federation across servers Some XEPs are optional/uneven
Proven to millions of connections across servers
Free (Ejabberd Community) You assemble the client stack
Who uses it: WhatsApp (originally), Nintendo Switch, Google Talk, Jitsi.
2. Matrix
The main modern open-standard rival. Server: Synapse (also Dendrite, Conduit). Client: Element . Instead of XMPP's live XML stream, Matrix syncs a replicated JSON event graph over HTTP — every message is an event, and history is a shared, eventually-consistent room state.
Pros Cons
--------------------------------------------------------------
Open standard, federated Heavier — Synapse is resource-
Strong built-in E2E encryption hungry vs Ejabberd
JSON over HTTP — familiar to devs Sync model uses more bandwidth,
Great for team/community chat worse fit for strict 2G budgets
Rich ecosystem, bridges galore Younger, protocol still evolving
Who uses it: the French government (Tchap), the German armed forces (BwMessenger), Mozilla , KDE , and many privacy-focused communities. It's the serious open alternative — but its "replay the room's event history" model is more bandwidth-hungry than XMPP's lean stanza stream, which matters when your users pay per megabyte on 2G. That single fact is a large part of why MaasaiChat chooses XMPP over Matrix.
3. MQTT
A lightweight publish/subscribe protocol from the IoT world. Broker: Mosquitto , EMQX, HiveMQ.
Pros Cons
--------------------------------------------------------------
Extremely lightweight wire format Not a chat protocol — no roster,
Tiny overhead, ideal for low presence, offline history, MUC
bandwidth / battery You build ALL chat semantics
Great pub/sub fan-out on top yourself
Simple to reason about No federation, no identity model
Who uses it: sensors, cars, smart devices — and famously Facebook Messenger , which used MQTT for years to get fast, low-overhead delivery on poor mobile networks. MQTT is a fantastic transport , but it gives you a pipe, not a chat system. You'd rebuild everything XMPP already provides. (Note: Ejabberd itself speaks MQTT natively, so you can even use both.)
4. Proprietary binary protocols
The big consumer apps mostly rolled their own closed protocols:
WhatsApp — started on Ejabberd/XMPP, later moved to a custom binary protocol using the Noise Protocol Framework for its handshake, with the Signal Protocol for end-to-end encryption.
Signal — the Signal Protocol over its own service; the gold standard for E2E encryption.
Telegram — its own MTProto protocol.
Discord — a custom WebSocket gateway protocol, backed by Elixir/Erlang (the same platform Ejabberd runs on).
Pros Cons
--------------------------------------------------------------
Fully optimized for one app You must design + maintain the
Smallest possible wire format entire protocol yourself
Total control No standard, no federation
Years of engineering
Wrong choice unless you're at
massive scale with a big team
Who uses it: WhatsApp, Signal, Telegram, Discord. Great if you're a well-funded platform. Not a starting point for a community app.
5. Hosted / Backend-as-a-Service
Buy chat as an API. Firebase (Firestore + Cloud Messaging), Stream , Sendbird , PubNub , Twilio Conversations .
Pros Cons
--------------------------------------------------------------
Fastest to ship Pay per user / per message forever
No servers to run You don't own the data
Handles scale for you Vendor can change pricing or
Nice SDKs cut you off
Data lives outside your country
Costs balloon as you grow
Who uses it: startups that want chat live this week. Wrong fit for a self-reliant community tool where every user is cost-sensitive and independence is the point — a pricing change in San Francisco should never be able to shut down messaging in Ngorongoro.
6. Raw WebSocket + your own protocol
Open a WebSocket, invent your own message format, build the rest by hand.
Pros Cons
--------------------------------------------------------------
Total freedom You reimplement routing, presence,
Simple to start ("just a socket") offline, groups, receipts, archive,
Familiar to web devs reconnection — for years
No standard, no interoperability
Every bug is yours to discover
Who uses it: Slack and Discord built custom protocols on top of WebSocket — but with large engineering teams. For a solo or small team, this is the "reinvent XMPP, badly" path.
The verdict for MaasaiChat
Own data Low 2G Built-in Free / Effort to
& control bandwidth chat feats cheap ship
--------------------------------------------------------------------------
XMPP/Ejabberd YES Excellent YES YES Medium
Matrix YES Fair YES Cheap-ish Medium
MQTT YES Excellent NO YES High
Proprietary YES Best NO No Very high
Hosted (SaaS) NO Varies YES No Low
Raw WebSocket YES Good NO YES Very high
XMPP is the only row that is yes on ownership, excellent on bandwidth, yes on built-in chat features, and free — at merely medium effort. For a self-hosted community app on 2G in East Africa, no other option matches on all four. That is why the rest of this book is XMPP.
1.7 Why XMPP fits rural East Africa specifically
Every decision in this book is shaped by one reality: MaasaiChat users are on 2G/3G, low-end Android, with connections that drop constantly . XMPP earns its place for concrete reasons.
Tiny messages. A stanza is a few hundred bytes. On a metered 2G plan where users pay per megabyte, that matters. No fat envelopes, no HTTP headers per message.
Built for connections that drop. Stream Management (XEP-0198, Chapter 10) is designed for exactly this. When Naserian rides out of coverage near Ngorongoro and back ten minutes later, her session resumes — messages that arrived while she was gone are delivered, and messages she sent that didn't quite make it are re-sent, with no full reconnect and re-authentication. This single extension is worth the whole protocol on a rural network.
Offline delivery is standard. If Ole's phone is off when Enkiama messages him, the server holds the message and delivers it when Ole reconnects. You don't build this.
One server, huge capacity. A single Ejabberd node has handled two million concurrent connections in production. MaasaiChat across two countries won't come close to stressing it — so it runs on modest, affordable infrastructure.
You own it. Self-hosted, open source, no per-message fees, no vendor who can cut you off.
Rural constraint XMPP answer
---------------------------------------------------------------
Expensive metered data --> Tiny stanzas, no polling
Connection drops constantly --> Stream Management (resume, XEP-0198)
Phone often off/asleep --> Server-side offline storage + push
Low-end hardware --> Lightweight client, one connection
Must be self-run --> Open protocol, free Ejabberd
1.8 The mental model to carry forward
Before the next chapter, lock in this picture:
Each user has an address (JID): naserian@maasaichat.com .
Each device opens one long-lived connection to maasaichat.com .
Over it flow stanzas — small XML fragments.
There are exactly three stanza types : message , presence , iq .
New features are added as extra XML inside stanzas , defined by XEPs , never by changing the core.
The server ( Ejabberd ) handles routing, offline storage, presence, groups, and archive so you don't have to.
Everything from here builds on those six facts.
✅ What you learned in this chapter
XMPP is an open, standardized protocol for exchanging small XML stanzas in real time over one long-lived connection .
It exists because HTTP request/response can't push server-initiated messages efficiently — fatal on metered, high-latency rural links.
The three stanza types are message , presence , iq ; features are added via XEPs without changing the core.
XMPP is the rulebook; Ejabberd is a builder that followed it — like HTTP↔Nginx or SQL↔PostgreSQL. Gajim, Smack, and sendxmpp are other builders following the same rules, which is why they all interoperate.
Real, current users include WhatsApp (built on Ejabberd), Nintendo Switch, Zoom, and Google Talk .
The alternatives are Matrix, MQTT, proprietary binary protocols, hosted SaaS, and raw WebSocket — each with real trade-offs; XMPP uniquely wins on ownership + bandwidth + built-in features + cost for a self-hosted 2G community app.
XMPP fits rural East Africa : tiny messages, Stream Management for dropping connections, standard offline delivery, one high-capacity self-hosted server, and full ownership.
MaasaiChat runs its own Ejabberd on maasaichat.com — no dependence on outside messaging providers.
Ready for next chapter? (Chapter 2 — The XMPP Vocabulary : every key term you'll meet in this book — stanza, JID, resource, stream, namespace, roster, presence, MUC, MAM, SASL, and the rest — each defined plainly with a MaasaiChat example, so no word is ever a mystery in the chapters ahead.)
Chapter 2 — The XMPP Vocabulary
Key Terms & Definitions
XMPP has a lot of vocabulary, and the deep-dive chapters ahead assume you know it. So this chapter is a dictionary you read once and refer back to forever. Every term gets a plain definition, an analogy, and — where it helps — a MaasaiChat example. You'll meet each of these again in depth later; the goal here is that no word is ever a mystery.
The terms are grouped the way they actually relate, not alphabetically:
A. The absolute core XMPP, stanza, stream, JID, resource
B. Addressing details bare/full JID, message types
C. Connecting & login TLS, SASL, bind, stream features
D. Staying connected Stream Management, ping, keepalive
E. Messaging features receipts, markers, chat states, IDs, carbons
F. Presence & contacts presence, roster, subscription
G. Group chat MUC, affiliation vs role, occupant
H. Storage & history offline messages, MAM, RSM, inbox
I. Discovery & extensions namespace, XEP, disco, caps, PubSub, PEP
J. Media, calls, push, E2E file upload, Jingle, push, OMEMO
K. Federation & transports s2s, dialback, BOSH, WebSocket
L. Ejabberd-specific mod_, ejabberdctl, Mnesia, vhost, ACL,
Erlang cookie, Erlang distribution
A one-page Quick Reference Card sits at the end of the chapter — tear it out (metaphorically) and keep it beside you.
A. The absolute core
XMPP
The rulebook. Extensible Messaging and Presence Protocol , an open IETF standard (RFC 6120/6121) created in 1999 as "Jabber." It defines how chat works: the shape of messages, the address format, how you log in, how features extend the core. It runs nothing by itself — a server like Ejabberd implements it. (See Chapter 1.)
Stanza
The basic unit of communication — one small XML fragment, like one sentence in a conversation. Everything you send or receive is a stanza. There are exactly three types: , , .
The cattle arrived safely at the river.
Stream
The single long-lived connection between client and server. A tunnel that stays open; every stanza flows through it. Opening it is like starting a phone call; closing it is hanging up.
... all stanzas flow here for hours ...
JID (Jabber ID)
The address of every entity in XMPP — users, servers, group rooms. Like an email address, but for real-time chat. Format: user@domain/resource .
ole@maasaichat.com/android a specific device
ole@maasaichat.com the person (any device)
maasaichat.com the server itself
warriors@conference.maasaichat.com a group room
Resource
The device-identifier part of a JID, after the slash. It exists because one person logs in from several devices at once, and the server must tell them apart.
ole@maasaichat.com/phone Ole's Tecno
ole@maasaichat.com/tablet Ole's tablet
Send to the bare JID ( ole@maasaichat.com ) and Ejabberd chooses the best device; send to a full JID ( .../phone ) and it goes to that one device only.
B. Addressing details
Bare JID vs Full JID
Bare JID — ole@maasaichat.com . Identifies the person . Used for offline messages, roster entries, MUC membership.
Full JID — ole@maasaichat.com/android . Identifies a specific connected device/session . Used to reach exactly one device, e.g. during a call.
Rule of thumb: person → bare, device → full.
Message types
The type attribute on a tells the server and client how to treat it:
chat one-to-one conversation (Naserian → Ole)
groupchat a MUC room message (to warriors@conference...)
normal a single message, no ongoing chat (system notices)
headline broadcast/alert, never stored offline (announcements)
error something failed; carries an child
Using the wrong type causes real bugs — e.g. a headline won't be saved for an offline user, so MaasaiChat uses chat / groupchat for anything that must survive a dropped connection.
C. Connecting & login
TLS / STARTTLS
Encryption of the stream. Before any password is sent, the client upgrades the plain TCP connection to an encrypted one (STARTTLS), or connects to an already-encrypted port (Direct TLS, 5223). No TLS = passwords and messages travel in the clear. MaasaiChat requires TLS always.
SASL
Simple Authentication and Security Layer — the login system, i.e. how you prove who you are. It supports several mechanisms :
PLAIN sends the password directly (only safe inside TLS)
SCRAM-SHA-1 secure challenge/response, no password on the wire
SCRAM-SHA-256 stronger
SCRAM-SHA-512 strongest classic option (what Gajim used)
X-OAUTH2 log in with an OAuth token instead of a password
With SCRAM, Ole's phone never sends his password — it solves a cryptographic challenge that proves it knows the password. (See also §J, channel binding.)
Stream features
Right after the stream opens, the server sends a list — "here's what's available/required next": STARTTLS, which SASL mechanisms, resource binding, Stream Management, and so on. The client walks through them in order. It's the server announcing the login menu.
Bind (resource binding)
After SASL proves who you are, binding assigns which session — it hands you your full JID by attaching a resource.
android
ole@maasaichat.com/android
Now Ole's full JID exists and messages can be routed to this exact session.
D. Staying connected (vital on 2G/3G)
Stream Management (SM) — XEP-0198
Makes delivery reliable on bad networks with an acknowledgement counter, and lets a dropped session resume instead of fully reconnecting.
"please confirm how many of my stanzas you got"
"confirmed — I have received up to stanza 32"
"I dropped and reconnected — resend from where we left off"
MaasaiChat example: Ole sends message #30 on 2G near the boma, the signal drops, he reconnects, the server sees he only acked #29, and re-sends #30. Zero loss. This is the single most important extension for rural networks.
Ping — XEP-0199
A tiny "are you still there?" iq . The client or server pings periodically to detect a silently-dead connection (common when a mobile network drops without closing the socket).
Whitespace keepalive
Even cheaper than a ping: the client sends a single space character down the stream now and then, just to keep NATs and carrier gateways from closing an "idle" connection. Common on mobile.
E. Messaging features
Delivery Receipt — XEP-0184
Proof a message reached the recipient's device — the single grey/blue tick "delivered."
Are the warriors ready?
Chat Markers — XEP-0333
Proof a message was received and read — the "read" tick. Distinct from a delivery receipt: delivery = it arrived on the device; marker (displayed) = the human actually saw it.
received landed on the device
displayed shown to the user (the "read" tick)
acknowledged app-level handled
Chat State Notifications — XEP-0085
The "typing…" experience. Tiny signals about what the other side is doing:
active looking at the chat
composing typing right now ("Naserian is typing…")
paused stopped typing but still there
inactive tab/chat idle
gone left the conversation
Stable & Unique Stanza IDs — XEP-0359
Gives every message two dependable IDs so all devices agree on identity: an origin-id set by the sender and a stanza-id assigned by the server/room. Essential for edits, reactions, replies, and de-duplication when the same message arrives via several paths.
Last Message Correction — XEP-0308
Editing a sent message. The new stanza points at the old one's ID with , and clients swap the display in place (the "edited" label).
Message Retraction — XEP-0424
"Delete for everyone." A stanza that tells clients to remove a previously-sent message, referenced by its ID.
Message Reactions — XEP-0444
Emoji reactions attached to a message ID (👍 on Ole's cattle update), rather than a new separate message.
Message Carbons — XEP-0280
Multi-device sync for one-to-one chats. When Naserian messages Ole, a copy is delivered to every one of Ole's connected devices, and copies of what Ole sends also appear on his other devices — so phone and tablet stay identical (like WhatsApp Web mirroring your phone).
F. Presence & contacts
Presence
An announcement of availability, broadcast to those allowed to see it.
available online (the default)
away stepped away
xa extended away (gone a while)
dnd do not disturb
unavailable offline
chatHerding near Ngorongoro
When Ole closes the app, the server sends unavailable on his behalf, and his contacts see him go offline.
Presence priority
When Ole is online on several devices, each presence carries a numeric . Messages to his bare JID go to the highest-priority device. Negative priority means "never auto-deliver here."
Roster
Your contact list — stored on the server , synced to every device. Switch phones, log in, and all contacts reappear instantly. Each entry holds a JID, a display name, subscription state, and groups.
Ole's roster:
naserian@maasaichat.com (Warriors)
enkiama@maasaichat.com (Elders)
nkeri@maasaichat.com (Traders)
Presence Subscription
Permission to see someone's presence — like a mutual follow. You must ask and be approved.
subscribe "I want to see your status"
subscribed "granted — you may see mine"
unsubscribe "stop seeing my status"
unsubscribed "denied / revoked"
Ole sends subscribe to Naserian; she returns subscribed ; now Ole sees when she's online.
Roster push
When your roster changes (you add Nkeri), the server pushes the update to all your logged-in devices automatically, so they stay in sync without asking.
G. Group chat
MUC (Multi-User Chat) — XEP-0045
Group chat rooms. Each room is itself a JID on the conference service:
warriors@conference.maasaichat.com
^room ^MUC service domain
Send with type='groupchat' and every occupant receives it.
Occupant & nickname
Inside a room you appear under a nickname , and your in-room address is room@service/nickname (a full JID whose "resource" is your nick). Your real JID may be hidden depending on room settings.
Affiliation vs Role (the classic MUC confusion)
Two different permission systems in every MUC:
AFFILIATION long-term membership status (persists across visits)
owner created the room, full control
admin can manage members/admins
member allowed into a members-only room
outcast banned
none no special standing
ROLE what you can do RIGHT NOW, this visit
moderator can kick, grant voice, moderate
participant can speak
visitor can only read (no voice)
none not in the room
Affiliation is who you are to the room over time ; role is what you may do in this session . Enkiama the chief might be an owner (affiliation) who is currently acting as moderator (role).
Message Moderation — XEP-0425
Lets a room moderator retract/hide someone else's message in the room (spam control), distinct from a user deleting their own.
H. Storage & history
Offline Messages — XEP-0160
If the recipient is offline, the server stores the message and delivers it on reconnect — voicemail for chat. Enkiama is out of signal in the bush; Ole's message waits on the server and lands when Enkiama's phone finds a tower.
MAM (Message Archive Management) — XEP-0313
The server keeps a searchable archive of conversations; clients fetch history on demand. Buy a new phone, log in, ask for "the last 7 days," and your full history appears. Without MAM, a new device starts empty.
RSM (Result Set Management) — XEP-0059
Paging for large result sets — "give me 20 messages before this point," then the next 20. MAM uses RSM so a phone on 2G loads history in small chunks instead of one huge download.
Inbox — XEP-0430
A server-built list of your conversations with the latest message and unread count for each — the "chat list" screen, computed server-side so it's instant and consistent across devices.
IQ (Info/Query)
The request/response stanza — XMPP's version of an HTTP GET/POST. Always in pairs, always with a matching id :
get ask for information result success (may carry data)
set do or change something error it failed
...contacts...
Roster fetch, ping, disco, bind, MAM queries — all are IQs.
I. Discovery & extensions
Namespace (xmlns)
A unique string that says which extension an element belongs to . Without it, is meaningless; with xmlns='urn:xmpp:receipts' everyone knows it's a delivery receipt. Think of it as the department stamp on a memo.
See xmlns="urn:xmpp:something" → it's a XEP.
Search that string → you find the exact XEP instantly.
XEP
XMPP Extension Protocol — a document that adds a feature on top of core XMPP, each with a number: XEP-0045 (group chat), XEP-0184 (receipts), XEP-0166 (calls). Base XMPP is a basic phone; XEPs bolt on the camera, caller-ID, and voicemail. (Full treatment in Chapter 6.)
Service Discovery (disco) — XEP-0030
How one entity asks another "what are you, and what can you do?" A client discos the server to learn which features and services (MUC, file upload, push) exist.
Entity Capabilities (caps) — XEP-0115
An optimization on top of disco: each client advertises a short hash of its feature set in presence, so others cache "a client with hash X supports these XEPs" instead of re-asking every time. Saves bandwidth — good on 2G.
PubSub (Publish-Subscribe) — XEP-0060
A general publish/subscribe system on the server: publishers post items to a node , subscribers get them. The backbone for many features (avatars, bookmarks, presence-like data).
PEP (Personal Eventing Protocol) — XEP-0163
A simplified PubSub attached to a user's own account — "my nodes." Used for things like your current avatar, mood, or bookmarks, auto-broadcast to contacts who care.
J. Media, calls, push, encryption
HTTP File Upload — XEP-0363
How you send photos/files. The client asks the server for an upload slot (a PUT URL + a GET URL), uploads the file over HTTPS, then sends the GET URL in a normal message. This is the file-transfer method MaasaiChat actually uses — it works fine over flaky mobile links, unlike peer-to-peer transfer.
Jingle — XEP-0166 (+0167/0176/0215/0320)
The signaling framework for voice/video calls. Jingle negotiates the call (who, what codec, network path) inside XMPP , while the actual audio/video flows over WebRTC. Related XEPs handle RTP media (0167), ICE network traversal (0176), TURN/STUN discovery (0215), and encryption (0320).
Push Notifications — XEP-0357
Wakes a sleeping phone. When a message arrives for an app that's backgrounded/disconnected, the server triggers a push (via FCM on Android) so the user gets notified without holding a socket open — essential for battery on low-end phones.
OMEMO — XEP-0384
Modern end-to-end encryption for XMPP (built on the Signal Protocol's ideas). Messages are encrypted per-device so that not even the server can read them. Optional; a design decision for later MaasaiChat phases.
SASL Channel Binding — XEP-0440 / Extensible SASL — XEP-0388
Hardening for login: channel binding ties the SASL authentication to the exact TLS connection, blocking a class of man-in-the-middle attacks. XEP-0388 modernizes how SASL mechanisms are negotiated.
K. Federation & transports
c2s and s2s
Two connection kinds Ejabberd listens for: c2s (client-to-server, port 5222 — phones connecting) and s2s (server-to-server, port 5269 — other XMPP servers connecting for federation).
Federation
Different XMPP servers talking to each other, so naserian@maasaichat.com could message someone@another-server.org — the same way email crosses providers. Enabled by s2s.
Server Dialback — XEP-0220
A verification handshake that lets a receiving server confirm a connecting server really owns the domain it claims, preventing spoofed federation. (Bidirectional s2s, XEP-0288, lets one connection carry traffic both ways.)
BOSH and WebSocket
Alternative transports for when a raw TCP stream isn't possible (e.g. a browser). BOSH tunnels XMPP over HTTP long-polling; WebSocket carries the XML stream over a WebSocket. Native mobile apps use raw TCP; web clients use WebSocket.
L. Ejabberd-specific terms
mod_ (module)
A plugin that adds a feature to Ejabberd, switched on in ejabberd.yml — like browser extensions.
mod_muc group chat mod_mam message archive
mod_offline offline storage mod_http_api REST API
mod_ping keepalive pings mod_mqtt MQTT protocol
mod_push push notifications mod_admin_extra extra CLI commands
No mod_muc → no group chat. Add it → group chat works.
ejabberdctl
Ejabberd's command-line control tool, run inside the container — the psql / redis-cli equivalent for Ejabberd.
docker exec ejabberd ejabberdctl status
docker exec ejabberd ejabberdctl registered_users maasaichat.com
docker exec ejabberd ejabberdctl send_message chat ole@maasaichat.com \
naserian@maasaichat.com "" "Meeting at the manyatta tonight"
vhost (virtual host)
One Ejabberd process can serve several domains at once ( maasaichat.com , staging.maasaichat.com ), each with its own users and settings — like Nginx server blocks.
ACL & Access Rules
Ejabberd's permission system: an ACL defines who (e.g. "admins = admin@maasaichat.com"), and an access rule grants those groups rights (e.g. who may use the REST API). Misconfigured ACLs are the classic cause of Account does not have the right to perform the operation when calling the API.
Mnesia
Erlang's built-in database, bundled with Ejabberd, used for its internal live state — who's connected where, room state, roster, short-term offline spool. It does not hold MaasaiChat's business data; that lives in PostgreSQL.
Erlang Cookie
A shared secret string all nodes in an Ejabberd cluster must have identical — the "password" that proves two nodes belong to the same cluster. Match → they trust each other; mismatch → the node is rejected.
Erlang Distribution (dist)
Erlang's native node-to-node networking. In a cluster, if Ole is on Node 1 and Naserian is on Node 2, Node 1 hands the stanza straight to Node 2 over Erlang distribution — no Redis pub/sub needed. This is why an Ejabberd cluster is simpler than a hand-built socket cluster.
Spool
The queue where offline messages wait for a disconnected user. When Enkiama reconnects, Ejabberd flushes his spool to his device.
Quick Reference Card
Term Simple definition
────────────────────────────────────────────────────────────────
XMPP The language/rules of chat
Stanza One unit of communication (message/presence/iq)
Stream The single open connection (tunnel)
JID Address: user@domain/resource
Resource Device identifier (/phone /tablet)
Bare / Full JID Person (bare) vs specific device/session (full)
Message type chat / groupchat / normal / headline / error
TLS / STARTTLS Encryption of the stream
SASL Authentication system (SCRAM, PLAIN, OAuth)
Stream features Server's "menu" of what to negotiate next
Bind Claiming your resource → full JID
Stream Management Reliable delivery + resume on bad networks (0198)
Ping "Are you alive?" heartbeat (0199)
Keepalive Whitespace to stop NAT/carrier timeouts
Delivery Receipt "Delivered" tick (0184)
Chat Markers "Read/displayed" tick (0333)
Chat States typing / paused indicators (0085)
Stanza IDs Stable message identity (0359)
Correction Edit a sent message (0308)
Retraction Delete for everyone (0424)
Reactions Emoji on a message (0444)
Carbons Multi-device 1:1 sync (0280)
Presence Online/offline status
Priority Which device gets bare-JID messages
Roster Contact list (stored on server)
Subscription Permission to see presence (mutual)
MUC Group chat room (0045)
Occupant / nick Your identity inside a room
Affiliation Long-term membership (owner/admin/member/outcast)
Role Current-session powers (moderator/participant/visitor)
Offline Messages Stored when recipient offline (0160)
MAM Server message archive/history (0313)
RSM Paging for large results (0059)
Inbox Server-built conversation list (0430)
IQ Request/response stanza (get/set/result/error)
Namespace (xmlns) Which XEP an element belongs to
XEP A document adding a feature to XMPP
disco Service discovery: "what can you do?" (0030)
caps Cached capability hashes (0115)
PubSub / PEP Publish-subscribe / personal eventing (0060/0163)
HTTP File Upload Send files via upload slot (0363)
Jingle Voice/video call signaling (0166)
Push Wake a sleeping phone via FCM (0357)
OMEMO End-to-end encryption (0384)
c2s / s2s Client-to-server / server-to-server
Federation Cross-server messaging (like email)
Dialback Verifies a federating server (0220)
BOSH / WebSocket XMPP over HTTP / over WebSocket (for browsers)
mod_ Ejabberd plugin/module
ejabberdctl Ejabberd CLI management tool
vhost One server hosting multiple domains
ACL / Access Rule Ejabberd permission system
Mnesia Ejabberd's internal live-state database
Erlang Cookie Cluster membership password
Erlang dist Direct node-to-node communication
Spool Queue of waiting offline messages
✅ What you learned in this chapter
Stanza, stream, JID, resource are the four words the whole protocol stands on — one XML unit, one open connection, one address, one device tag.
Addressing splits into bare (person) vs full (device) JIDs, and every message carries a type (chat/groupchat/normal/headline/error) that decides how it's handled.
Login is a pipeline: TLS → SASL → bind → stream features , and staying connected on 2G relies on Stream Management, ping, and keepalives .
Messaging polish is a stack of small XEPs — receipts (0184), markers (0333), chat states (0085), stable IDs (0359), carbons (0280) — plus edit/retract/react.
Presence + roster + subscription run contacts and status; MUC runs groups, where affiliation (long-term) and role (this session) are different things .
History is offline messages → MAM → RSM → inbox ; discovery is disco + caps ; extensions are identified by namespace and documented as XEPs .
Ejabberd-specific: mod_ modules, ejabberdctl, vhosts, ACLs, Mnesia, the Erlang cookie, and Erlang distribution — the operational vocabulary for Part 3.
Ready for next chapter? (Chapter 3 — XML Basics for XMPP : now that you know the words, we slow down and read the XML itself — elements, attributes, namespaces, and the wrapper — so every stanza in the book is effortless to parse by eye.)