# Watch cost arrive live

A server-sent tail of costed rows — what it costs, how a slow viewer is handled, and why it shows only rows the file holds.

Source: https://tokentriage.com/docs/build/observability-live-feed

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

Some questions are answered by a graph and some by watching. Whether your headers are arriving,
whether a new deployment is hitting the model you expected, whether the first requests after a
config change are being priced: those are all easier to see in a live tail than to query for.

The dashboard's Live surface is that tail. Each costed request appears as a row a moment after it
completes, with its model, its dollar and its provenance.

## Turn it on

The Live feed is part of the dashboard, which is off in a config file and on in the zero-config
quickstarts.

```yaml
dashboard:
  enabled: true # off by default in a config file
  stream:
    client_queue: 1024 # per-viewer buffered events; default 1024
    max_clients: 32 # concurrent viewers; default 32
```

Then open the admin plane and go to the Live surface.

<Screenshot
  stem="live-feed"
  url="localhost:9090/ui/live"
  alt="The Live surface: costed decision rows arriving one after another, each with its model, dollar and provenance."
  caption="The Live feed — costed rows arriving over a server-sent stream, each stamped with its provenance (real dashboard, demo mode)."
/>

## What it can and cannot show

The stream is fed by a tee that fires after a row has been appended to the ledger, which gives it
one property worth relying on: **every row you see in the feed is already in the file.** The feed
cannot show you a row that failed to write, cannot read ahead, and cannot synthesize one.

The consequence runs the other way too. A row can be in the file and not in your feed, if your
viewer fell behind. So the feed is a sample of the ledger rather than a second copy of it, and it
is not the thing to count from.

Alongside the rows, the stream carries a periodic health event. That event is where
`declog_dropped_total` surfaces outside the metrics endpoint, and it is what the byte-fidelity
indicator reads: it is green when the fail-open counter and the passthrough-error counter are both
zero, and those numbers come from the running proxy rather than from a constant.

## What it costs

Two bounded resources, both configurable.

**A queue per viewer.** Each connected browser gets a buffer of `client_queue` events. Rows are
handed to it without blocking; when the buffer is full the event is discarded for that viewer,
counted on `tokentriage_dash_stream_dropped_total`, and disclosed to that viewer as an in-stream
`dropped` notice. A viewer that cannot keep up degrades visibly rather than slowing anything down.

**A cap on viewers.** `max_clients` bounds concurrent connections, and a connection past the cap is
turned away with a message saying so rather than being accepted and starved.

Neither one can reach your traffic. The tee runs after the write, it cannot block the writer, and
a dead subscriber is dropped rather than backpressuring anything.

<Callout type="warning">
A tab left open on a busy instance is the usual cause of a rising
`tokentriage_dash_stream_dropped_total`. It means that viewer missed events; it does not mean data
was lost. Reload the tab, and read [what a drop means](/docs/build/observability-drops) before
treating the counter as an incident.
</Callout>

## Reconnecting without gaps or duplicates

The stream supports `Last-Event-ID`. Each row event's id is the row's coordinate in the ledger
file, so a viewer that drops its connection reconnects with the last id it saw and the server
replays the byte range after it, straight out of the file. A row that arrives live during the
replay is de-duplicated against the replayed range rather than shown twice.

One case cannot be replayed exactly, and it says so instead of pretending. If the log rotated
while you were disconnected, the coordinate names a file that is now compressed and has no
seekable offset, so the server emits a notice describing the gap rather than silently resuming
from the new file's start.

## What can go wrong

**The surface is missing.** The dashboard is off, or the stream is not wired into this build. A
route that exists without its feature answers with a message naming the cure rather than a bare
404.

**Rows appear with no tenant or agent.** The feed shows what is on the row. Empty attribution means
the headers did not arrive, not that the feed dropped them; check
[identity on the wire](/docs/build/observability-headers).

**Nothing appears at all and traffic is flowing.** Requests are reaching a different replica, or
the dashboard is reading a different decision-log path from the one the proxy is writing. Both
paths are in the same config file, so compare them directly.

<Details summary="Why the feed is not a substitute for the metrics endpoint">

They fail differently and that is the point. The feed is per-viewer, unbounded in what it shows
and bounded in how much it can buffer, so it degrades under load exactly when you most want to be
watching. The counters are aggregated, cheap, and scraped on somebody else's schedule.

Use the feed for the first five minutes after a change, when you want to see individual rows and
you know what you are looking for. Use [the metrics](/docs/build/observability-metrics) for
anything you want to be told about while you are asleep.

</Details>

<NextSteps items={[
  {title: "The trust dashboard", href: "/docs/build/dashboard", description: "The other dashboard surfaces: Overview, Requests, Traces, Cost, Providers, Pricing, Routing, Alerts and Governance."},
  {title: "What a drop means", href: "/docs/build/observability-drops", description: "The stream's drop counter next to the three others, and which of them can move a total."},
  {title: "Scrape the metrics endpoint", href: "/docs/build/observability-metrics", description: "The surface that tells you about a problem when nobody is watching a tab."},
  {title: "Trace one request end to end", href: "/docs/build/observability-request-id", description: "When a row in the feed looks wrong, this is how to open it up."},
]} />
