arizuko

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, /edit and /v1/round_done, with a service:routd ES256 bearer. Identity headers from proxyd.
Outputs
HTML pages and SSE frames to the browser, and posts to /v1/messages on routd with the own service:webd token 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/messages is 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, :8080 by default.
WEBD_URL
The URL that routd calls back, http://webd:8080 by default.
ROUTER_URL
Where routd is, http://routd:8080 by default.
AUTHD_URL, AUTHD_SERVICE_KEY
webd exchanges the key for the service:webd ES256 token that it presents on /v1/*. The JWKS from AUTHD_URL verifies the callbacks of routd on /send and /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