> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.thatdevguy.in/llms.txt
> Use this file to discover all available pages before exploring further.

# The /_anvil Dashboard: Inspect Traces and Agent Costs

> Anvil's built-in observability UI shows trace trees, token usage, and per-run costs in your browser — no extra tooling required.

Anvil ships a self-contained observability dashboard that renders trace trees, token usage, and cost per run directly in your browser. In development, `anvil dev` serves it automatically at `/_anvil`. In production, mount it yourself as middleware so you control who can reach it.

The dashboard reads from the same `TraceStore` you pass to your `Tracer`, so there is no separate sink to configure — traces written during a request are visible the moment the run ends.

## What the dashboard shows

* **Trace list** — every agent run, sorted by most recent, with name, status badge, duration, token count, and cost.
* **Span tree** — drill into any trace to see the nested agent → model → tool → retrieval hierarchy, with per-span attributes (model, provider, token usage, tool inputs/outputs) and error details.
* **Token usage and cost** — rolled-up `totalInputTokens`, `totalOutputTokens`, and `totalCostUsd` displayed per trace and per span.

The page polls the trace list every three seconds, so it stays current during an active development session without any WebSocket setup.

## Enabling in production with dashboardMiddleware

For deployed environments, import `dashboardMiddleware` from `anvil/trace` and add it to your root `_middleware.ts`:

```ts theme={null}
import { dashboardMiddleware } from 'anvil-sdk/trace';
import { SqliteTraceStore } from 'anvil-sdk/trace';

const store = await SqliteTraceStore.open('.anvil/traces.db');

export default dashboardMiddleware(store);
```

Mount it alongside your auth middleware to protect it:

```ts theme={null}
import { dashboardMiddleware } from 'anvil-sdk/trace';
import { SqliteTraceStore } from 'anvil-sdk/trace';
import { requireInternalToken } from '../lib/auth.js';

const store = await SqliteTraceStore.open('.anvil/traces.db');

export default [
  requireInternalToken,
  dashboardMiddleware(store, { path: '/_anvil' }),
];
```

<Warning>
  In production, always protect the dashboard with authentication middleware. The dashboard exposes full trace data including tool inputs, model outputs, and cost information that should not be publicly accessible.
</Warning>

## dashboardMiddleware arguments

`dashboardMiddleware` accepts a `TraceStore` as its first argument and an optional `DashboardOptions` object as its second.

The first positional argument is the trace store to read from. Wire this to the **same** `SqliteTraceStore` (or other `TraceStore`) instance you passed to your `Tracer`, so the dashboard and the tracer share one underlying database.

`DashboardOptions` has one field:

<ParamField body="path" default="/_anvil" type="string">
  The URL prefix at which the dashboard is mounted. The middleware intercepts all requests whose path starts with this value. Change it if `/_anvil` conflicts with an existing route.
</ParamField>

## Sharing the store with the Tracer

The dashboard and the tracer must point at the same `TraceStore` instance. The idiomatic pattern is to open the store once at application startup and pass it to both:

```ts theme={null}
import { Tracer, SqliteTraceStore, dashboardMiddleware } from 'anvil-sdk/trace';
import { defineAgent } from 'anvil-sdk/agent';
import { openai } from 'anvil-sdk/llm';

// Open once — shared by tracer and dashboard.
const store = await SqliteTraceStore.open('.anvil/traces.db');

export const tracer = new Tracer(store);

// In your root _middleware.ts
export const middleware = dashboardMiddleware(store);

// In your agent route
export const post = defineAgent({
  client: openai({ model: 'gpt-4o' }),
  tracer,
  // ...
});
```

## Dashboard API routes

The middleware also exposes a small JSON API used by the built-in UI:

| Route                        | Description                                                            |
| ---------------------------- | ---------------------------------------------------------------------- |
| `GET /_anvil`                | Serves the self-contained dashboard HTML (no external assets)          |
| `GET /_anvil/api/traces`     | Lists traces (spans omitted for payload efficiency). Accepts `?limit=` |
| `GET /_anvil/api/traces/:id` | Returns a single trace with the full span tree                         |

You can query these endpoints directly from your own tooling or scripts.

<Tip>
  Pair the dashboard with the [Cost Governor](/observability/cost-governor) to see budget breaches in the trace tree, and with the [OpenTelemetry exporter](/observability/otel) to forward the same traces to Datadog, Grafana, or Langfuse alongside the local view.
</Tip>
