# What a new dashboard shows you

Before any traffic arrives, every screen has something to say. What an empty instance renders, measured on a real binary rather than the demo.

Source: https://tokentriage.com/docs/build/dashboard-first-run

---
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 turned the dashboard on and pointed nothing at the proxy yet. Most products give you
a blank grid here. This one has something to say on every screen, and what it says is designed
to be checkable: it tells you what it looked at, how many rows that was, and what it therefore
cannot claim.

That matters more than it sounds, because the failure it avoids is specific. A dashboard that
paints sample data on an empty install teaches you to distrust every figure you see later. The
screens below are captures from a genuinely empty ledger on a binary running from a config
file, not from the demo, so what you see here is what you will see.

## The Overview on an empty ledger

<Screenshot
  stem="dashboard-first-run"
  dark={true}
  url="localhost:9090/ui/"
  label="Empty ledger"
  caption="The Overview of a config-file instance with zero rows in its ledger. Nothing here is sample data: the ledger it read is empty."
  alt="The Overview surface on an empty instance. A banner reads: no data in the ledger yet, TokenTriage is waiting for your first request, with a Send your first request button. Spend to date and projected period-end both read UNPRICED rather than a dollar figure. Savings vs baseline reads: no verdict yet. Every panel carries a 0 rows chip, the charts say no series evaluated 0 rows, and the anomalies panel reports no anomalies detected with the parameters it used."
/>

Read it from the top. The banner states the situation in a sentence and offers the one action
that changes it. The trust-ladder rail on the left shows three rungs with none of them
claimed. Then the panels, and this is where the interesting decisions are.

**Spend to date reads `UNPRICED`, not `$0.00`.** Those are different claims. Zero dollars
would assert that requests were priced and cost nothing. `UNPRICED` says no priced row exists
to total. The same rule governs a populated instance where some models are missing from the
price table, so you learn it here and it holds everywhere. [Where a cost number comes
from](/docs/concepts/honesty-model) is the rule; this is the rule with no rows in it.

**Savings vs baseline says there is no verdict yet**, and names what would produce one: a
graded shadow comparison. It does not show zero savings, because nothing has been measured.

**Each panel carries its row count.** `0 rows` beside a chart, `evaluated 0 rows` inside it.
On a populated instance the same chips carry the estimated and unpriced counts, so the count is
not an empty-state affordance. It is the disclosure that is always there.

**The anomaly panel publishes its parameters even with nothing to report.** It names the
detection method, the fields it is watching and the fields it is not, and the window it
evaluated. "Nothing was found" and "nothing was examined" are different answers, and the panel
distinguishes them.

<Callout type="key">
An empty dashboard is a measurement, not a placeholder. Every figure that is absent says why
it is absent, and the row counts beside each panel let you tell "nothing happened" apart from
"nothing was read".
</Callout>

## The Live surface is the on-ramp

<Screenshot
  stem="dashboard-live-waiting"
  dark={true}
  url="localhost:9090/ui/live"
  label="Empty ledger"
  caption="The Live surface before the first request, on the same empty instance. The snippet carries this instance's own proxy address."
  alt="The Live surface on an empty instance, headed: waiting for your first request. A tabbed snippet picker offers env, wrap, curl, openai-python, openai-node, anthropic-python, and migration snippets from LiteLLM, Portkey, Helicone and OpenAI. The selected snippet sets the Anthropic base URL to this instance's proxy address. Below, a trust-ladder checklist shows ledger verified as satisfied and shadow running, verdict green and routing available as not yet met, each naming the endpoint it reads."
/>

This is where a new instance points you, and it is the most useful screen on an empty
dashboard. It carries a snippet picker with your instance's own address already in it, in the
shape your client uses, including the migration forms if you are coming from another gateway.
Copy one, send a request, and the row appears on this screen as it is costed.

Under it sits the trust-ladder checklist, and each item names the endpoint it was computed
from. "Ledger verified" is satisfied because the read model is reading your decision log.
"Verdict green" is not, and it says why in the product's own words: no graded rows under the
current policy yet. That is the same gate [Routing](/docs/build/routing)
owns, rendered as a checklist rather than a refusal.

Get started's [first live request](/docs/get-started/first-live-request) is the tutorial for
that first call. This page is what the screen looks like while you make it.

## What the other surfaces say with nothing to show

Each surface writes its own empty state, and they fall into three kinds. Telling them apart is
the useful skill, because only one of the three is a problem.

**Nothing has happened yet.** The subsystem is on, it read your ledger, and there was nothing
in it. Requests reports `No rows match — evaluated 0 rows in scope`. Pricing reports `No
unpriced rows — every logged row has a price`, which is true of zero rows. Anomalies reports
its scan with a population of zero.

**The subsystem is off.** The surface says so and names the setting. Cache says no cache is configured on this instance, so there is no hit rate or saving to
show. Cost breakdown: `Cost-optimization analytics is not enabled on this instance, so there is no
breakdown to show — not an empty one. Set analytics.enabled: true (with analytics.rollups.enabled
and dashboard.index.enabled) and restart.` Governance and Calibration do the same. These
surfaces are usually absent from your sidebar entirely; you reach this state by typing the URL.

**Nothing could be read.** Rarer, and the one worth acting on. The Realized surface makes the
distinction explicitly: `the applied-recommendation list could not be read from this instance,
so no measurements are shown — this is a failed read, NOT a report that nothing has been
applied.` A failed read is not an empty result, and a surface that conflated them would be
telling you a comfortable lie.

<Details summary="The empty state of every surface, as measured">

Captured by walking each route on a `run --config` instance over a zero-row ledger.

| Surface | What it says |
|---|---|
| Overview | banner offering the first request; `UNPRICED` spend; no savings verdict |
| Requests | `No rows match — evaluated 0 rows in scope` |
| Traces | `No trace selected — open a request's trace from the Requests feed` |
| Cost | `UNPRICED` total; `Excludes 0 unpriced rows from all totals; 0 rows are estimated` |
| Reconcile | `No invoice has been imported on this instance yet`, naming the accepted formats |
| Realized | a failed read, distinguished from nothing having been applied |
| Providers | one card per upstream, health `unknown`, `no reference` where no key env var is set |
| Pricing | the active snapshot with its model count; `0 rows unpriced` |
| Saved views | three empty panels, each with its create form |
| Live | the snippet picker and the trust-ladder checklist |
| Routing | the ladder at Ledger; every quality source `CANNOT_MEASURE`, `0 / 8 labeled` |
| Policy | one blank rule, the emitted YAML, and the validate-diff-apply controls |
| Honesty | `Promote gate not satisfied`, with the reason and the reproducing command |
| Calibration | `No calibration directory is wired on this engine` |
| Anomalies | the scan with its parameters and a population of zero |
| Budgets | `No budgets defined. Create one below` |
| Fleet | `Single-node fleet — fleet not configured — showing the local instance only` |
| Settings | the running config, its generation number, and any config warnings |

The Honesty surface also embeds a captured reference sample of `eval` output, labelled as
one, so you can see the shape of a verdict before you can produce a verdict of your own.

</Details>

## Panels that are not yours

Four panels currently render a fixed example instead of your instance's data, and two of them
carry no watermark. This is a defect, it is filed, and until it is fixed you need to know
which panels to disregard.

| Where | What it shows | Marked as sample? |
|---|---|---|
| Cost, divergence panel at the bottom | a cohort comparison over 812 rows, naming tenants `acme` and `globex` | yes, `DEMO DATA` |
| Alerts, burn-alert backtest | a replay over 128 records with rules that are not yours | yes, `DEMO DATA` |
| Fleet, coverage of the last merged read | `Partial coverage — 2 of 3 replicas answered`, listing `tt-0`, `tt-1`, `tt-2` | no |
| Governance · limits, preview and tenant panels | a key `vk_team_ml_01` under `org:acme` with dollar figures | no |

<Callout type="warning">
The Fleet coverage panel will tell a single-node install that two of three replicas answered.
Read the roster above it, which is live and reports one replica correctly, and disregard the
coverage panel until this is fixed. The same goes for the limit preview on Governance ·
limits: those keys and dollar amounts are a fixed example, not your governance state.
</Callout>

The rest of the dashboard does not behave this way, and it used to. Every per-surface data
loader once fell back to the same sample corpus whenever a fetch failed, which meant one
declined request could repaint a live instance with fabricated dollars. That was found,
filed, and closed: the fallback is now compiled out of production builds, and a failed load
degrades to an empty panel that discloses its own incompleteness. The four panels above sit
outside that fix because they take their sample as a default value rather than as a fallback
after a failed fetch.

## How to check that what you see is yours

Two checks, both quick.

**Look for the watermark and the mode chip.** A demo instance labels itself in the header and
puts a diagonal `DEMO DATA` watermark across every spend tile. If your header does not say
demo and a panel does, that panel is one of the four above.

**Ask the instance directly.** The capabilities endpoint reports what the server thinks it
is, and it needs no authentication:

```console
$ curl -s http://127.0.0.1:9090/tt/api/v1/capabilities
{"api_version":2,"engine":{"version":"dev","mode":"ledger","pricing_snapshot":"litellm@2026-07-14#179affb"},"features":{"index":true,"stream":true,"control_plane":true,"replay":false,"budgets":{"defined":0,"enforce_granted":false},"alerts":false,"anomaly":true,...
```

That is the first 300 characters of a much longer document, from the same empty instance the
captures came from. Two fields answer the question: `demo` is `false` further down, and a
query returns a row count you can compare against:

```console
$ curl -s -X POST http://127.0.0.1:9090/tt/api/v1/query \
    -H 'content-type: application/json' -d '{"metrics":["requests"]}'
{"provenance":{"pricing_snapshots":[],"rows":0,"estimated_rows":0,"unpriced_rows":0,"demo_rows":0,"truncated":false,"source":"scan","indexed_through":{"file":"","offset":0,"ts":""},"window":{"since":"","until":""}}}
```

`rows: 0` with `demo_rows: 0` and `truncated: false` is a successful read of an empty ledger.
`truncated: true` would mean the read did not complete, which is the third case above.

## What this page does not cover

It covers what an empty instance renders, not how to fill it.
[Your first live request](/docs/get-started/first-live-request) is the tutorial that produces
the first row, and [the trust ladder](/docs/concepts/core-concepts) explains the three rungs
the checklist is tracking.

<NextSteps items={[
  {title: "Your first live request", href: "/docs/get-started/first-live-request", description: "Point one call at the proxy and watch the row land on the Live surface."},
  {title: "Why your sidebar is shorter", href: "/docs/build/dashboard-navigation", description: "Several of the surfaces above are absent rather than empty. Here is what decides which."},
  {title: "How to read a number", href: "/docs/build/dashboard-reading-a-number", description: "The chips and flags in these captures, and what each one licenses you to claim."},
  {title: "Surface index", href: "/docs/build/dashboard-surfaces", description: "Every screen with the question it answers and where its subject is documented."}
]} />
