You want one AI agent that you can talk to from WhatsApp, Telegram, Slack, your terminal, and your phone — all at once, all sharing memory. The architectural answer is a single long-lived process: the gateway. This lesson builds the mental model that the next six lessons stand on.
Picture what you actually want. You have an assistant. You'd like to message it from your phone's WhatsApp on the bus, from Slack at work, and from your laptop terminal at home — and you want it to be the same assistant each time, with the same memory, the same tools, the same ongoing conversation. Not seven disconnected bots. One brain, many mouths.
Now try to build that the obvious way, with no central piece. Each chat app would need its own little program holding its own connection and its own copy of the conversation. Seven apps × three of your devices is a tangle of independent, stateful connections that have no idea about each other. Your terminal can't see the conversation your phone is having. Worse: some of these connections are jealous — WhatsApp, for instance, lets exactly one client hold a session at a time. Two programs fighting over it get you logged out.
The fix is the oldest idea in distributed systems: stop wiring everything to everything. Put a hub in the middle. One process owns every provider connection. Everything else — your terminal, your phone app, the web UI — becomes a thin client that connects to the hub and asks it to do things. The slider below shows why this matters as you scale.
Left, the no-hub world: every message surface (a chat app) wired directly to every client (a device/UI) — that's a full mesh, and the connection count grows like the product N×M. Right, the gateway: every surface and every client connects only to the hub — the count grows like the sum N+M. Drag the slider and watch the left side choke.
That single decision — one process owns the connections, everything else is a client of it — cascades into every other lesson in this series: the protocol those clients speak (Lesson 2), how the hub decides who is allowed to connect (Lesson 3), how it runs the agent (Lessons 4–5), and how it keeps overlapping messages from colliding (Lessons 6–7). Get the gateway picture and the rest has a place to live.
The gateway is a daemon — a program that starts once and keeps running in the background, indefinitely, with no terminal attached. (You may have met daemons before: sshd serves SSH, dockerd runs containers. The "d" is for daemon.) You start it with a command like openclaw gateway, and from then on it lives in the background, holding all the connections.
Why must it be long-lived rather than spun up per request? Because the things it holds are themselves long-lived. A logged-in WhatsApp session, an open WebSocket to Telegram, the in-memory state of an ongoing agent run — none of these survive being torn down and recreated for every message. The daemon is the container that keeps them alive between messages.
Here is the concrete failure, because "single-owner session" sounds abstract until it bites. WhatsApp Web binds your account to one active client. If a second program logs in with the same credentials, the first is kicked, reconnects, kicks the second, and so on — a flapping loop that WhatsApp's anti-abuse systems read as suspicious and can answer by banning the number. So "one gateway owns the session" is not a stylistic choice; it is the price of not getting locked out. We'll make this invariant concrete — and enforce it in code — in Chapter 6.
Because the daemon is the single point that everything depends on, you don't usually babysit it by hand. You hand it to a supervisor — launchd on macOS, systemd on Linux — whose entire job is "keep this one process alive: start it at boot, restart it if it crashes." One process, supervised, owning everything. That's the spine.
127.0.0.1:18789. The 127.0.0.1 part (loopback) means "this machine only": by default the gateway is reachable from programs on the same computer and nobody else. That default is a security decision we'll unpack in Lesson 3; for now just note that the gateway is a server, and clients reach it at an address.
If the gateway owns everything, then everything else is a client of it. This is the second big idea: the macOS app, the command-line tool, the web UI, your automations — none of them are "the system." They are all just clients that connect to the one gateway and ask it to act. Swap the app for the CLI and you are talking to the very same brain.
Borrowing language from networking, we split the world into two planes. The control plane is where intent flows: "run the agent on this message," "show me the chat history," "change this setting." The data plane is where the heavy, stateful work actually happens: the provider connections, the running agent, the session transcripts. Clients live in the control plane; the gateway is the data plane. Thin clients, fat hub.
This operator/node split is worth pausing on, because it inverts the usual direction. An operator asks the gateway to do things. A node lets the gateway do things on it. The desktop app is an operator. Your phone, paired so the agent can snap a photo, is a node. The same physical laptop can even connect as both, in two separate connections with two different roles. We'll give nodes their own chapter (Chapter 4), because the abilities they expose are exactly where the security weight sits.
One more surface belongs here: the gateway can also serve a little web UI of its own. OpenClaw calls this the canvas host — static pages it serves under internal paths (you'll see URLs like /__openclaw__/canvas/). The point isn't the path; it's that "a browser tab showing the agent" is, again, just another operator client of the same gateway. Everything is a client. There is only ever one hub.
Now look at the other side of the hub: the chat apps. OpenClaw calls each one a surface — WhatsApp, Telegram, Slack, Discord, Signal, iMessage, and a built-in WebChat. Each is a place a human message can surface into the system. And each speaks a completely different dialect.
WhatsApp is driven through Baileys; Telegram through its Bot API (OpenClaw uses a library called grammY); Slack and Discord have their own SDKs and event shapes; Signal and iMessage are different again. A "new message" event from Telegram looks nothing like one from Slack. If the rest of the gateway had to know about all seven dialects, it would be an unmaintainable mess.
What is that single internal vocabulary? The gateway emits a small set of event kinds that everything downstream understands — among them chat (a message moved), agent (the agent produced output), presence (who's online), health (is the system OK), heartbeat and cron (timed beats and scheduled jobs). Seven dialects in; one clean event stream out.
Here is the move that makes a hub a hub, though: when a message arrives on one surface, the gateway doesn't just hand it to the agent — it can fan it out to every interested client. Your phone app and your desktop app both subscribe; both light up. This is the multiplexing we promised in Chapter 0, made real: one inbound message, normalized once, delivered to many. Let's build exactly that wiring.
Operators tell the gateway what to do. Nodes do the opposite: they let the gateway reach out and act in the physical or local world through a device. A node is a phone, a Mac, an Android tablet, or a headless box that connects to the gateway and says, in effect, "here are the things I can do for you."
What it can do is declared precisely at connect time, in three nested lists — and the nesting is the whole security story:
camera, screen, location, voice, canvas, talk. "I am the kind of device that has a camera."camera.capture or system.run. If a command isn't on this list, the node refuses it outright.screen.record, camera.capture. A command can be allowlisted yet still blocked because its permission was never turned on.When the agent wants the node to do something, the gateway issues a node.invoke. The node checks the request against its own declaration: is this command on my allowlist and is the required permission granted? Both must be true. Either alone is not enough. This is capability-based security — a node can only ever do what it explicitly advertised it would do, nothing more.
system.run command, then "the agent" can run shell commands on that Mac. That is RCE — remote code execution — on a real machine you own. The capability declaration is the fence around it. This is also why, in Lesson 3, we'll treat pairing a node as one of the highest-trust actions in the whole system: you are handing the agent a pair of hands.
Let's implement the gate itself — the check that runs on every node.invoke. It's small, but getting it exactly right (both conditions, no shortcuts) is the difference between a fence and a hole.
camera.capture but the camera.capture permission was never granted. What happens on node.invoke?A client that only ever asks the gateway questions would be blind between questions. But things happen on their own: a message arrives, a node goes offline, the agent finishes thinking. So the gateway doesn't just answer requests — it pushes events to its connected clients, unprompted, the moment something changes. That's how your desktop app updates the instant a WhatsApp message lands without you refreshing.
We met the event vocabulary in Chapter 3; now watch it flow. The gateway continuously emits a stream: chat when a message moves, agent when the agent speaks, presence for who's online, health for system status, and a steady heartbeat/tick that says "I'm still alive." Each event carries a sequence number — a simple counter, 1, 2, 3, … — so a client can tell what order things happened in.
Why design it this way? Keeping a perfect, replayable, per-client event log is expensive and fiddly — you'd need durable storage, retention policies, and acknowledgements. "Detect a gap, then refetch the current truth" is dramatically simpler and is always correct, because the snapshot you refetch is the real current state, not a reconstruction. You trade a little extra work on the rare gap for a system that can never quietly drift out of sync. Play with it:
The gateway (left) emits numbered events that flow to a client (right), which tracks the last sequence number it saw. Press Drop an event to make one go missing in transit. When the next event arrives with a skipped number, the client detects the gap and refreshes — it doesn't ask for the lost event back.
This "refresh on gap" rule looks like a small networking detail, but it shapes how every client in the system is written: assume your view can go stale, watch the sequence numbers, and be ready to resync from scratch. We'll see the exact wire-level machinery — the sequence fields, the keepalive tick, the reconnect logic — in Lesson 2.
An invariant is a rule the system promises is always true, no matter what. The gateway's whole design rests on three of them. Naming them gives you a checklist for reasoning about any agent gateway — and each one, broken, is a specific real failure.
connect message. Anything else — non-JSON, or a different message first — gets the connection hard-closed immediately. Broken (if it weren't enforced): unauthenticated or malformed clients could poke at the system before proving who they are.Invariant 2 is your first taste of the protocol mindset we'll live in next lesson: the server is strict on purpose. A connection that doesn't immediately speak the expected first word is not coddled or corrected — it's dropped. Strictness at the door is cheaper and safer than guessing at intentions later.
Invariant 1 is the one we can make concrete right now. "Exactly one owner" is a classic mutual-exclusion problem: a shared resource (the session) and competitors (gateway processes) where letting two in at once corrupts everything. Let's watch it break, then fix it with the one rule that makes it hold.
connect message. By Invariant 2, what happens?Here is the whole picture in motion. Surfaces on the left, the gateway in the middle, operator clients and capability nodes on the right. Press Inbound message to watch a message surface on a random app, flow into the gateway, fan out to every operator client, and come back as an agent reply. Toggle the Mac node to see a capability host appear. This single diagram is the map for the next six lessons.
Teal = message surfaces (chat apps). Orange = the gateway daemon. Blue = operator clients. Green = capability nodes. Watch an inbound message normalize at the hub and fan out; the small ticks on the gateway are presence/health heartbeats.
agent event fires: intake → context → inference → tools → reply.node.invoke makes physical.