# The dashboard

A web UI your binary already contains, reading the same ledger the CLI reads. What it is for, and where each screen is documented.

Source: https://tokentriage.com/docs/build/dashboard

---
import Callout from '../../../components/Callout.astro';
import Details from '../../../components/Details.astro';
import NextSteps from '../../../components/NextSteps.astro';
import Screenshot from '../../../components/Screenshot.astro';

You have a decision ledger with a line per request, and reading it with `tokentriage top`
works but does not scale to the questions people actually ask: which team spent that, why did
this request go to the expensive model, is the saving I reported measurable. The dashboard is
those questions as screens.

It ships inside the binary you already downloaded. No separate service, no database, no build
step, no network call to anyone. Turn it on and it serves a web UI on the admin port over the
same file the CLI reads, so the two cannot disagree about a total.

This page is the section's front door: what the dashboard is, what it is not, and how the rest
of these pages are organised. If you want to see it before reading about it, `tokentriage demo`
boots the whole thing against synthetic traffic with no credentials.

## One ledger, two front ends

The CLI and the dashboard are readers of the same decision log. Neither computes a number the
other cannot; a dollar on a screen came from the same row, priced against the same pricing
snapshot, as the dollar the CLI prints. Where the dashboard adds value is in what a file is bad
at: filtering, grouping, drilling from a total into the rows that produced it, and watching
rows arrive.

Two properties follow from shipping it inside the binary, and both matter operationally.

**It sits on the admin plane, not the request path.** Your data plane
(`server.listen`, default `127.0.0.1:8787`) proxies traffic. The dashboard is served from
the admin listener (`server.admin`, default `127.0.0.1:9090`) alongside `/health` and
`/metrics`. A dashboard query, a slow chart, even a crashed browser tab cannot touch a request
in flight. If the admin listener fails to bind at boot, the proxy keeps serving and the banner
says the dashboard is unavailable.

**It fetches nothing.** The web app is compiled into the executable and served with a strict
content-security policy that permits same-origin requests only. No CDN, no font host, no
telemetry. That is what makes it work unchanged in an air-gapped deployment, and it is why the
whole UI is one `go:embed` directory rather than a `docker-compose` file.

<Screenshot
  stem="overview"
  dark={true}
  url="localhost:9090/ui/"
  alt="The Overview surface over a populated ledger: spend to date and projected period-end as dollar tiles, a savings-vs-baseline verdict, spend over time, and top spend broken down by routing tier, agent and model. A mixed-snapshot chip warns that totals span two pricing snapshots."
  caption="Overview, over the demo ledger. The chips beside each panel report how many rows the figure came from, how many were estimated, and how many carried no price (real dashboard, demo mode)."
/>

## What it is not

A short negative list saves more time than a feature tour.

**It is not a place to configure your proxy by hand.** Every write goes through the same
config-as-contract loop the CLI uses: the surface composes YAML, you validate it, diff it
against what is running, then apply it under a compare-and-set guard. The dashboard never
mutates config behind your back, and a rejected apply shows the server's error rather than a
friendly summary of it.

**It is not a monitoring system.** It reads your ledger; it does not scrape, store time
series, or page anyone. Prometheus and OTLP are how the gateway joins the monitoring you
already run, and [Observability & tracing](/docs/build/observability) owns that.

**It is not multi-tenant SaaS.** There is no organisation model, no seat billing, and no
hosted copy of your data. Access control is three roles on one instance, plus read-only share
links for a frozen query.

**It is not the system of record.** The ledger is. Everything on screen is derived from it
through a SQLite read model you can delete at any time, which is the subject of
[the read model](/docs/build/dashboard-read-model).

## How this section is organised

Most of what you see on a dashboard screen has a meaning that belongs to a subsystem: a cost
figure means what [Cost & attribution](/docs/build/cost-analytics) says it means, a verdict
chip means what [the honesty model](/docs/concepts/honesty-model) says it means. Duplicating
those definitions here would give you two places to be wrong.

So this section documents **the application** — turning it on, getting into it, what decides
which screens exist, and the rules every screen obeys — and sends the meaning of any number to
the section that owns it. One page carries the map: the
[surface index](/docs/build/dashboard-surfaces) lists every screen with the question it
answers, the setting that makes it appear, and a link to the concept behind it.

If you are looking at a screen right now and want to know what it is, start there. If you are
setting the dashboard up, the pages run roughly in the order you need them: turn it on, see
what a new instance shows, work out why your sidebar is shorter than the screenshots, then
sign in properly before you expose it to anyone.

<Callout type="key">
Everything on these screens is derived from your decision ledger and computed on your own
machine. A number the ledger cannot support is not rendered — figures that no price table
covers are excluded from totals and disclosed as a count, rather than being shown as `$0.00`.
</Callout>

## What it costs to run

The dashboard is off in a config file and on in the zero-config quickstarts, which surprises
people often enough to be worth stating twice. `dashboard.enabled` defaults to `false`, so a
config-file deployment adds two lines to get a UI. `tokentriage run --anthropic` and
`--simple/--complex` turn it on for you, and `--no-dashboard` opts back out.

Running it costs one SQLite file, a bounded query budget, and a small amount of memory for
the live-stream fan-out. It reads your ledger continuously to keep the index current. It does
not add latency to proxied requests, because it is not on that path.

<Details summary="What the dashboard adds to a deployment, item by item">

- A **read-model index** at `dashboard.index.path`, defaulting to
  `~/.tokentriage/index/decisions.sqlite`. Derived from the ledger, rebuildable, disposable.
- A **state directory** at `dashboard.state_dir`, defaulting to `~/.tokentriage/dash`, holding
  the users file, token hashes, saved views, the audit log and the event ring.
- An **HTTP surface** on the admin listener: the UI at `/ui/` and the management API at
  `/tt/api/v1`.
- A **bounded query budget**: `dashboard.query.deadline` (10s), `row_cap` (1000) and
  `max_concurrent` (2), so one expensive filter cannot monopolise the instance.
- A **live stream** with `dashboard.stream.client_queue` (1024) and `max_clients` (32).

Every one of those has a default, and the defaults are what a two-line `dashboard:` block
gets you.

</Details>

## Where to go next

Turning it on takes one setting; the page below covers the ports, the container, and the
demo. If you would rather understand the shape first, the surface index is the map of every
screen, and reading a number is the one page that pays off on every other screen.

<NextSteps items={[
  {title: "Turn on the dashboard", href: "/docs/build/dashboard-enable", description: "The setting, the ports, Docker, and the demo that needs no credentials."},
  {title: "Surface index", href: "/docs/build/dashboard-surfaces", description: "Every screen: the question it answers, what makes it appear, and where its concept is documented."},
  {title: "What a new dashboard shows you", href: "/docs/build/dashboard-first-run", description: "The empty states on a genuinely empty ledger, measured on a real binary rather than the demo."},
  {title: "How to read a number", href: "/docs/build/dashboard-reading-a-number", description: "The chips, flags and disclosures that appear beside every figure on every surface."}
]} />
