arizuko › components › gated
gated is the gateway daemon. One Go binary that owns messages.db, runs the poll loop that turns inbound rows into agent runs, serves the HTTP API channel adapters register against, and hosts the per-group MCP unix sockets the in-container agents talk back through.
It is the only daemon that runs SQL migrations. Other daemons connect to the same SQLite file but never write to the schema.
Adapters (Telegram, Slack, WhatsApp, web…) need somewhere to post inbound messages and pick up outbound replies. Agent containers need an MCP socket to call tools and submit turns. A scheduler needs a place to drop future messages. All those needs collapse to one writer with one schema.
Without gated nothing has a backend. Adapters cannot register, the poll loop never fires, no container ever spawns, the dashboard reads stale rows, and the schema drifts the moment two daemons each try to migrate it. gated centralises the writes so the rest of the system can stay thin.
On startup gated opens $STORE_DIR/messages.db, runs every migration in store/migrations/, brings up the HTTP server, and enters the gateway poll loop. Each tick:
messages rows newer than the last cursor.routes table.#observe, store-only. Otherwise enqueue a container run for that group.adapters (teled, slakd, webd, …)
| POST /v1/messages (signed by CHANNEL_SECRET)
v
gated ----> messages.db (single writer, WAL)
|
| spawn per-group container
v
arizuko-ant container
| MCP over unix socket
v
gated /v1/outbound ----> adapter callback URL
Inputs: HTTP from adapters on $API_PORT; MCP from agent containers on per-group unix sockets under $HOST_DATA_DIR/ipc/<folder>/. Outputs: spawned arizuko-ant containers, outbound rows in the DB, HTTP callbacks back to each registered adapter.
Hard deps: a writable data dir, Docker (for the container runner), and $CONTAINER_IMAGE built and present locally.
Beyond the message bus, gated owns the slow-changing config rows — groups, acl, acl_membership, routes, web_routes, scheduled tasks, secrets, network rules. Each is one Go struct registered with the resreg engine. From that single struct the engine serves four surfaces that cannot drift, because they all read the same fields:
/v1/<name> — list, create, update, delete.arizuko export / arizuko apply./openapi.json.Declared foreign keys keep the rows honest: deleting a group cascades to its web_routes and route_tokens, so URL routes pinned to a removed group go with it. See reference/openapi for the per-daemon endpoints and reference/cli for the manifest commands.
Yes. gated is a plain Go binary that needs a data dir and an env file. The arizuko CLI generates a compose file that runs gated next to its adapters, but the daemon does not require compose.
# build
make build # produces ./gated
# seed a data dir
mkdir -p /srv/data/myinstance
cat > /srv/data/myinstance/.env <<EOF
CHANNEL_SECRET=$(openssl rand -hex 32)
STORE_DIR=/srv/data/myinstance/store
HOST_DATA_DIR=/srv/data/myinstance
CONTAINER_IMAGE=arizuko-ant:latest
API_PORT=8080
EOF
# run
cd /srv/data/myinstance && /path/to/gated
gated reads .env from its working directory (via core.LoadConfig), so always cd into the data dir before launching it. GET /health returns 200 once the API server is up and the DB is reachable.
API_PORT — HTTP listen port (default 8080).CHANNEL_SECRET — HMAC shared with every adapter; signs /v1/* requests.STORE_DIR — directory holding messages.db.HOST_DATA_DIR / HOST_APP_DIR — paths bind-mounted into spawned containers.CONTAINER_IMAGE — image used for per-group agent runs.CONTAINER_TIMEOUT, IDLE_TIMEOUT, MAX_CONCURRENT_CONTAINERS — runner caps.Full list and defaults in reference/env.
gated/README.md — tables owned, federated /v1/* surface, health signal.gateway/README.md — the poll loop, per-turn XML envelope, mute mode.store/README.md — schema, migrations, public API.ARCHITECTURE.md — full message flow and container lifecycle.