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

# Operations

> What you do after init — inspecting the local store, catching drift, previewing re-index costs, and the Docker-only path.

# Operating SchemaBrain over time

`init` got you a working agent. This page covers what you do after — inspecting the local store, catching drift before it shows up as bad agent answers, previewing re-index costs, and the Docker-only path.

For live observability (tail, audit log, OTel, PII refusal), see [`docs/observability.md`](observability.md).

## See your schema (dashboard)

`schemabrain dashboard` opens a read-only, `127.0.0.1`-only view of the
indexed store — a Knowledge Graph of your entities and joins, the PII
matrix, refusals, the audit log, a (copy-the-CLI) policy view, and drift.
It requires the `[ui]` extra:

```bash theme={null}
pip install "schemabrain[ui]"
schemabrain dashboard --store-path ./schemabrain.db
```

The dashboard reflects what's in the store — it never writes to your
database or edits your YAML. Full surface tour:
[`docs/dashboard/overview.mdx`](dashboard/overview.mdx).

## Inspect what's indexed

See what the agent has — same view it has, no LLM call, no source connection:

```bash theme={null}
schemabrain inspect
```

> **Your output will vary.** Entity names come from Sonnet's read of your schema (you'll typically see `user` not `customer` on the bundled fixture); join names follow your actual FK constraints. Operate on what `inspect` prints, not the names in this sample.

```
◆ store · ./schemabrain.db
12 tables · 84 columns · 12 entities · 5 metrics · 11 joins

Definitions
├── Entities (12)
│   ├── api_key
│   ├── billing_profile
│   ├── invoice
│   ├── payment_method
│   ├── plan
│   ├── session
│   ├── subscription
│   ├── subscription_item
│   ├── support_ticket
│   ├── usage_event
│   ├── user
│   └── workspace
├── Metrics (5)
│   ├── total_revenue
│   ├── active_subscriptions
│   └── … (3 more)
└── Joins (8)
    ├── workspace_users
    ├── subscription_plan
    └── … (6 more)

Drill into one: `schemabrain inspect <name>`
```

Drill into one entity for the full detail view — columns, PII tags, and the joins that reach it:

```bash theme={null}
schemabrain inspect user
```

```
◆ public.users · entity:user · binding id

Description:  A workspace member account (login credential + contact details).

Columns:
  id             bigint       not null  pk identity  public
  workspace_id   bigint       not null              public
  email          text         not null              pii (contact)
  full_name      text         not null              pii (contact)
  password_hash  text         not null              pii (credential)   ← catastrophic; never reaches the agent
  role           text         not null              public
  created_at     timestamptz  not null              public

Related entities:
  session    incoming  many_to_one  via `user_sessions`
      user.id = session.user_id
  workspace  outgoing  many_to_one  via `workspace_users`
      user.workspace_id = workspace.id
```

This is the operator's counterpart to the agent-facing MCP tools — anything `describe_entity` returns to Claude, `inspect` shows you locally.

Exit codes: `0` rendered, `1` drilled name not found, `2` operational refusal.

## Detect drift

`schemabrain check` walks every persisted entity, metric, and canonical join and confirms each one still matches the live source schema. Two classes of change surface as a structured drift report — before they become bad agent answers:

* **Structural** — a referenced table or column was dropped/renamed (`table_missing`, `identity_column_missing`, `measure_column_missing`, `time_dimension_column_missing`, `join_column_missing`).
* **Shape** — a column still exists but its type or nullability changed since you indexed (`type_mismatch`, `nullability_change`), compared against the indexed baseline. These catch the silent-correctness traps a pure existence check misses (e.g. a numeric measure column flipped to `text`, so `SUM`/`AVG` now errors or coerces). Shape drift is reported without cascade-suppressing dependent definitions.

```bash theme={null}
schemabrain check --url-env DATABASE_URL --store-path ./schemabrain.db
```

```
8 entities (7 healthy) · 12 metrics (11 healthy) · 5 joins (5 healthy)

  ✗ entity   customer
        identity_column_missing  public.customers.legacy_email
        → update entity 'customer'`s `identity:` field and re-run
          `schemabrain entities apply`

2 drifts detected.
```

Exit `0` when everything lines up, `1` when at least one drift is detected, `2` for operational refusals. Drift cascading is suppressed — when an entity's bound table is missing, downstream metric and join drifts on that table are suppressed so the output stays focused on root cause.

Pipe-friendly: `schemabrain check --url-env DATABASE_URL --json | jq '.exit_code'`.

## Preview the cost of catching up

Schedule re-indexes confidently. `schemabrain index --dry-run --since <duration>` previews what a real run would cost — no DB writes, no LLM calls, no `ANTHROPIC_API_KEY` required — and adds a freshness audit showing how much of the local store is stale relative to the chosen cutoff:

```bash theme={null}
schemabrain index --url-env DATABASE_URL --store-path ./schemabrain.db \
    --dry-run --since 14d
```

```
Would index 87 table(s): 4 changed, 83 unchanged, 0 removed. Columns: +12/~6/-0. Estimated LLM: 18 descriptions ($0.0054). Estimated embeddings: 18. No changes made to the store.
Stale since 14d: 42 columns across 9 tables (estimated refresh $0.0126)
```

The "changed/unchanged" line accounts only for the source diff since the last `index` run; the "Stale since" line flags columns whose owning table was last enriched before the cutoff — useful for catching tables that haven't been re-indexed even though they haven't structurally drifted. Accepts compact durations (`30s`, `5m`, `2h`, `14d`) or ISO 8601 timestamps with timezone.

## Run via Docker

If you don't want a host Postgres install at all, the repo ships a `docker-compose.yml` that brings up a Postgres container with the bundled fixture, indexes it, and leaves you with a populated store on a named volume:

```bash theme={null}
docker compose up
```

> **Note on ports.** The compose stack binds Postgres to host port **5433** (not 5432) so it never clashes with a developer-local Postgres already running on 5432. The MCP wiring below talks to the container over the internal Docker network (`postgres:5432`), so the host-side port mapping doesn't matter for the Claude Desktop integration.

Point an MCP host at the indexed store via `docker run`:

```jsonc theme={null}
// ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "schemabrain": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "--network", "schemabrain_default",
        "-v", "schemabrain_sb-data:/data",
        "-e", "DATABASE_URL=postgresql+psycopg://postgres:local@postgres:5432/postgres",
        "schemabrain:local",
        "serve", "--url-env", "DATABASE_URL", "--store-path", "/data/store.db"
      ]
    }
  }
}
```

The `docker compose up` recipe builds SchemaBrain from the repo's `Dockerfile`, so a checkout is all you need. A pre-built multi-platform image (`linux/amd64` + `linux/arm64`) on a public registry is on the v0.3.x roadmap so you can skip the build step.

Full Docker setup (env-var hygiene, host-uid mapping, containerised serve config) is in [`/setup/docker`](/setup/docker).
