arizuko › components › dashd
dashd
dashd is the operator control panel at /dash/: a hub of daemon tiles plus the pages that span daemons, and it owns no table.
dashd is the control panel of the operator
dashd is the control panel for the person who runs the instance. Open /dash/ in a browser to see groups, messages and tasks. There you can also edit routes, grants and some files of the agent, and answer held tool calls.
dashd has two parts. The hub at /dash/services/ is a grid of tiles, one per daemon, and each tile links to the own page of that daemon. The cross-daemon pages live in dashd itself: every page that spans daemons or covers the whole instance. A page about the runtime of one daemon lives in that daemon. routd serves /dash/routd/, runed serves /dash/runed/, and so on, each from handlers next to its own /v1.
Without dashd, only an engineer can read the instance
Without dashd, an operator inspects an instance through the /v1 API of each daemon, journalctl -u arizuko_<inst> and the arizuko CLI. That works for an engineer, but it is hard for anyone else, and it never shows on one screen what the instance does now.
dashd reads some tables directly and asks for the rest
The figure shows every path that dashd uses. The left box is what dashd reads and writes with its own store key. The right box is what it asks other daemons for over HTTP.
browser
│ /dash/...
▼
proxyd: auth "user", strips and stamps X-User-*, adds service:proxyd
│ /dash/<daemon>/ ──────────────► that daemon's own page
│ /dash/ and the rest
▼
┌──────────────────────────────────────────────────────────────┐
│ dashd: guard (ProxydTransit), then requireUser, requireAdmin,│
│ requireVisible or requireOperator │
└──────┬──────────────────────────────┬────────────────────────┘
│ own store key │ service:dashd bearer,
│ StoreReach: routd, onbod │ empty folder claim
▼ ▼
┌──────────────────────────┐ ┌───────────────────────────────┐
│ routd tables │ │ routd /v1: groups, tasks, │
│ acl for every gate │ │ routes, acl, route_tokens, │
│ admin writes, AsUser │ │ cost/summary, messages, │
│ portal counts, packages, │ │ sessions, principals, │
│ cooldowns, route joins │ │ routing, engagement, │
│ secret and env key names │ │ pending_actions, audit │
│ onbod tables │ │ onbod /v1: invites, audit │
│ invite writes │ │ runed, authd /v1/audit │
└──────────────────────────┘ │ proxyd /v1/proxyd_routes │
│ whapd /v1/pair │
└───────────────────────────────┘
groups/<folder>/ files: memory pages, skill toggles, group bootstrap
The split has a reason. dashd holds a store key for the tables of routd and onbod and for no others. Writes go directly to the store, because AsUser puts your sub on the audit row, in the same batch as the write. A call over /v1 would carry only service:dashd, and routd accepts a forwarded user only from proxyd. Reads go over the /v1 of the owner wherever the owner serves one. Your own secrets page is the exception. The secrets resource of routd is write-only on purpose. So dashd lists the key names from the table, and routd publishes no API that lists secrets.
Identity comes from proxyd. Every /dash/* handler, read views included, runs through guard. guard checks the service:proxyd transit bearer before it trusts X-User-Sub and X-User-Groups. So a request that does not pass proxyd never reaches a handler. Only /health, /openapi.json and the htmx asset stay public. Admin writes also run requireAdmin, which needs an admin grant on the folder, and a same-origin CSRF check.
The hub checks every daemon on each visit
Each visit checks the /health of every daemon at the same time, with a 500 ms timeout, so one slow daemon cannot stop the page. A tile shows one of three states:
ok- The daemon answered with 2xx.
err- The daemon refused, timed out or answered with something other than 2xx.
unknown- The name does not resolve, so the container is not deployed.
No “up but denied” state exists, because /health is public on every daemon. davd has no /health, so its tile checks GET /.
A tile is a link only if proxyd routes its /dash/<daemon>/. dashd asks /v1/proxyd_routes on proxyd: the route is the registration, and dashd discovers nothing. If proxyd does not answer, the hub links only the core daemons, so you never open a 404. A red dot never hides a link, because the daemon that is down is the one you want to open.
dashd runs alone with a port and the store
dashd needs a port and access to the tables of routd: the store server of the instance. Point AUTHD_URL at authd, and dashd verifies the transit bearer of proxyd against its key set on every /dash/* request. Without authd, for local development, the guard lets every request through. For this reason, put proxyd in front of dashd on the public internet. proxyd runs the login flow and sets the X-User-* headers that dashd trusts.
export DATA_DIR=/srv/data/arizuko_demo
export DASH_PORT=:8080
./dashd
# browse http://localhost:8080/dash/
DYNAMODB_URL names the store server, and dashd reads the tables of routd and onbod from it. DATA_DIR locates the groups tree at $DATA_DIR/groups/, which is files on disk. GET /health returns 200 as soon as the process is up. A store that does not open shows as a red banner on the pages that need it, not as a failed health check.
Ten pages read and ten pages write
Read views:
/dash/- The portal index, with links to the other pages.
/dash/status/- Groups, sessions and channels on one screen. For the operator, it also shows which chats got no answer.
/dash/activity/- The most recent messages across all groups.
/dash/tasks/- Scheduled tasks.
/dash/groups/- Group folders with their channel routes.
/dash/memory/- The editable markdown view of each group.
/dash/packages/- Installed packages and blended products from
installed_packages, with folder, name, source, revision and installation time. The page is read-only. Thearizuko packagecommands select and install packages. /dash/profile/- The subs linked to your login.
/dash/usage/- Model spend and turn counts per folder. The spend comes from the model gateway through
/v1/cost/summaryon routd. An instance with no gateway has no meter, and the page says so and does not show a zero. /dash/audit/- The audit trail, newest first, with filters for category, actor and folder. It merges the trails of routd, runed, authd and onbod. Each serves its own
audit_logatGET /v1/audit, and dashd shows the merged rows with a Source column. onbod is a source only where its store exists, because onboarding is optional, and a daemon that you never deployed is not a failed source. The “older” cursor keeps one position per source. Rows of two owners can share a timestamp, and a cursor on time alone would drop some. A source that does not answer gets a red banner with its name, and the rest still show. An audit page that says nothing about runed when runed did not answer would mislead you.
Write pages, which need an admin grant and pass a CSRF check:
/dash/routes/- The message-routing table: which folder an inbound message goes to. For the operator, it also lists the unrouted chats: every chat that messaged the instance and reaches no group. A claim button writes the rule.
/dash/groups/new,/dash/groups/{folder}- Create a group, and change its settings, grants and skill toggles, or delete it.
/dash/tasks/- Create, pause, resume and cancel tasks. The create form takes a cron expression. It refuses a one-time RFC3339 time, which the
schedule_tasktool of the agent accepts. /dash/tokens/{folder}/- List and revoke route tokens. No dashboard page creates one.
/dash/me/secrets,/dash/me/env,/dash/me/connections- Your own capability tokens, your model keys and the “Connect GitHub” OAuth connection.
/dash/approvals/- Held tool calls that wait for a person, limited to the folders where you hold
admin. /dash/engagement/- Every conversation that the agent is staying in without being addressed, with a disengage button. Only operators can open it.
/dash/proactive/- Which groups can speak first. The page is read-only, because
mode:stays in theCLAUDE.mdof the group. /dash/invites/- Create and revoke invites. Only operators can open it.
/dash/channels/whatsapp/pair- Pair WhatsApp again with a code. Only operators can open it. dashd calls
/v1/pair/starton whapd and writes an audit row.
When an owner does not answer, the page shows a red banner with its name, never an empty table. “No routes” or “costs nothing” are statements you would act on, and they are false when the source did not reply.
dashd owns no table and sends no messages
dashd does not send messages, create tokens or show the runtime of another daemon. Runs, keys, the proxy route table and the onboarding queue are on the pages of their own daemons. dashd owns no table: every write goes into a table of routd or onbod.
Two pages cover the details
- reference/env: dashd: the full table of environment variables.
- components/proxyd: the auth gate in front of dashd.