Dashboards
A dashboard is a pair of Bun services — a frontend and a backend — under
~/.adi/mono/dashboards/<id>/, authored as loose .ts files. It’s a hive service under
the hood (the per-user dashboards hive supervises both halves), so it gets a real <host>.adi
address the moment it’s created.
Why it exists
Section titled “Why it exists”Use a dashboard for results tables or job controls. Add panels in frontend/modules/<name>.ts
and endpoints in backend/routes/<name>.ts; changes hot-reload, with no build step and no
deploy.
Panel: /dashboards. API: POST /api/dashboards/create ({ "name", "description", "template" })
— runs the chosen template (below) and leases ports; both services start on the first request.
Leave template out for a dependency-free scaffold with no build step.
Edit frontend/modules/*.ts and backend/routes/*.ts directly; those two directories are the
only ones a template or migration never touches. Read the scaffold’s own README.md for the
exact shape a module or route has to export.
TemplatesExpert area
Section titled “Templates”Creating a dashboard runs a shell command — there’s no compiled-in scaffold. Each template is
defined in ~/.adi/mono/templates/<id>/template.toml:
label = "Vite + React 19 + Hono"engine = "bash"requires = ["bun"]command = 'bun create adi-family/dashboard-vite-react "$ADI_DASHBOARD_DIR" && bun "$ADI_DASHBOARD_DIR/scripts/adi-init.ts"'id is the directory’s name, never a field in the file. engine says how command runs —
"bash" (bash -c '<command>') is the only one today. requires lists the CLIs command
needs on PATH. Creation fails before the command runs if a required CLI is missing; the error
names it. command is the literal shell command — nothing substitutes into its text, so
there’s no ref or version field; a template pins whatever it wants inside command itself.
command runs with four environment variables, and nothing else is passed to it:
| Variable | Value |
|---|---|
ADI_DASHBOARD_DIR | the directory the command must create |
ADI_DASHBOARD_ID | the new dashboard’s id |
ADI_DASHBOARD_HOST | its <id>.adi host |
ADI_TEMPLATE_DIR | ~/.adi/mono/templates/<id>/, for a template’s own files/ |
It’s responsible for the whole dashboard, including its own .adi/hive.yaml — the API only
writes config.toml’s name, description, and [created_from] (the template id and the
exact command run) once the command succeeds. [created_from] is informational only; nothing
reads it back, and nothing ever rewrites a created dashboard afterwards.
Two templates are included: plain (dependency-free) and vite-react. They run bun create
against adi-family/dashboard-plain and adi-family/dashboard-vite-react, respectively.
GET /api/dashboards/templates lists every template installed on this machine, with which ones
this machine is actually able to run.
A create can fail three ways: an unknown template id is a 400 listing the ids that do exist; a
missing requires CLI is a 400 naming it, before anything runs; a command that exits non-zero is
a 500 — the half-made directory is removed, and the response carries the command’s own output.
Installing moreEnterprise interest
Section titled “Installing more”Review template.toml as you would any script. The marketplace installs
templates by copying their folders into templates/ when requested by id; nothing else about
creating a dashboard changes. Point the marketplace at your own bundle repository to offer only
templates you’ve written or vetted, instead of the two included ones.
One origin, relative paths onlyExpert area
Section titled “One origin, relative paths only”A dashboard is one hostname: the frontend owns /, the backend claims /api on the same
host. A panel calls ctx.api.get("/status"), never http://127.0.0.1:<port>/status and never
http://<host>.adi/status — no host or port is ever written into a panel, a route, or the
shell. That’s what makes the exact same dashboard work locally at <host>.adi, over
the mesh at <host>.<node>.n.adi, and behind a real domain later, for every viewer,
with no substitution.
Archiving
Section titled “Archiving”POST /api/dashboards/archive, then /delete — delete refuses unless the dashboard is
archived first, so removing one is always two deliberate steps.