arizuko › components › webd
webd
webd serves the web chat, the webhook intake and the operator panel, and streams each reply to the browser over SSE.
webd puts the agent in a web browser
webd serves the chat box on a web page and streams each reply back while the agent writes it.
webd is the web channel adapter. It registers with routd as the channel web, with the JID prefix web:. It sends the output of each turn to browsers over Server-Sent Events, a one-way live stream from the server to the browser. It serves three URLs: the public chat widget at /chat/<token>/, the webhook intake at /hook/<token>, and the operator panel with login at /panel/<folder>. Tokens come from the route_tokens table. /slink/* URLs answer with a 308 redirect to /chat/*.
For routd, webd is a normal channel adapter with the same protocol as teled or slakd. For a browser, webd is an HTTP server with one open SSE stream per active chat.
The web has no client of its own, so webd is that client
Telegram and Discord have their own clients. The web does not. To talk to an arizuko group from a browser, a program must host HTML and accept a POST. It must also push agent output to the page and look like one more channel to routd. A separate adapter keeps HTML out of routd and puts every web client on one code path. The clients are the built-in widget, the JS SDK and MCP clients of other parties.
Without webd, there is no browser chat, no slink widget and no operator MCP over HTTP. The dashboard still works, because dashd is a separate daemon, but end users can reach the agent only through a platform adapter.
Web chat uses plain HTTP and SSE
Web chat uses plain HTTP and no WebSockets. The browser posts a message and gets back a turn_id. It then opens an SSE stream at the URL of the round and reads event: message frames as the agent produces them. The stream closes on event: round_done. A dropped connection reconnects with Last-Event-Id, and webd sends the missed frames again.
webd sits between proxyd and routd
browser / curl / MCP client
│ HTTP + SSE
▼
proxyd (auth for /api/*, /mcp ; optional on /chat/*)
│
▼
webd ── POST /v1/messages ──► routd (service:webd ES256 bearer)
▲ │
│ ▼
└── POST /send ────────── the agent's guest
(routd calls webd back; webd fans to SSE subscribers)
- Inputs
- HTTP from browsers and MCP clients. HTTP callbacks from routd on
/send,/editand/v1/round_done, with aservice:routdES256 bearer. Identity headers from proxyd. - Outputs
- HTML pages and SSE frames to the browser, and posts to
/v1/messageson routd with the ownservice:webdtoken of webd. - Dependencies
- A reachable routd at
$ROUTER_URL, and the tables of routd: the/api/*chat reads of webd read them directly. A move of these reads to/v1/messagesis deferred.
webd runs alone only next to a routd
webd runs as a process, but it is of no use without routd. webd does not store messages: it forwards them. Run it alone only with a running routd.
# with routd already up on the same data dir
cd /srv/data/myinstance
WEBD_LISTEN=:9001 \
WEBD_URL=http://localhost:9001 \
ROUTER_URL=http://localhost:8080 \
AUTHD_URL=http://localhost:8081 \
AUTHD_SERVICE_KEY=$(grep ^AUTHD_SERVICE_KEY .env | cut -d= -f2) \
WEB_HOST=localhost:9001 \
/path/to/webd
At startup, webd calls POST $ROUTER_URL/v1/channels/register with the name web, the capabilities send_text, edit and delete, and its own callback URL. If the registration fails, the process exits. GET /health returns 200 after the registration.
An /edit from routd publishes a frame with the same id and the new text. So the browser replaces the status message and does not add a second one. webd shows no typing indicator and does not advertise typing.
Six environment variables configure webd
WEBD_LISTEN- The HTTP listen address,
:8080by default. WEBD_URL- The URL that routd calls back,
http://webd:8080by default. ROUTER_URL- Where routd is,
http://routd:8080by default. AUTHD_URL,AUTHD_SERVICE_KEY- webd exchanges the key for the
service:webdES256 token that it presents on/v1/*. The JWKS fromAUTHD_URLverifies the callbacks of routd on/sendand/edit. WEB_HOST,ASSISTANT_NAME- Values that webd renders into the chat widget and the URLs it issues.
webd shares no secret with proxyd. webd trusts the identity headers on /api/*, /x/* and /mcp only behind the service:proxyd bearer, which it checks against the same JWKS with auth.ProxydTransit. reference/env has the full list and the defaults.
Three pages cover the details
- [webd/README.md]: the full HTTP endpoints, the token contract, the limits of the SSE hub and the blocking-poll transport.
- components/route tokens: the public chat and webhook URLs that webd hosts.
- concepts/routing: how routd sends a message to the agent.