# Take it to production

The decisions that separate a working install from one you can leave running — in the order they stop being reversible.

Source: https://tokentriage.com/docs/operate/production-checklist

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

You have TokenTriage running and costing requests, and now something depends on it. This is the
list of what to settle before that becomes true, ordered so the choices that get harder to
change later come first.

Each item is one line here and a page elsewhere. Work down the list; nothing below depends on
you having read the pages above it.

**Prerequisites:** an install you have already made and used. [Installing
TokenTriage](/docs/get-started/installation) owns getting the binary or the image; this page
starts after that.

## Before the first request

These four are the ones that are painful to revisit, because two of them are immutable on a
running deployment and two of them decide what your history contains.

- [ ] **Pick a topology and know what it gives you.** One binary, a container, Compose, the
      Helm chart, Kustomize or Terraform. Each renders the same single-node shape, and the
      chart is the canonical artifact. [Deployment topologies](/docs/operate/deployment)
- [ ] **Put the ledger on ReadWriteOnce block storage.** Never NFS, EFS or any shared
      filesystem: rotation unlinks the active file, and a second writer on a shared mount loses
      decisions silently. [Back up and restore](/docs/operate/backup-restore)
- [ ] **Size the volume from your own row size, not ours.** Multiply requests by measured bytes
      by retention window. `persistence.size` cannot be grown by an upgrade, because the claim
      template is immutable. [Sizing and capacity](/docs/operate/capacity)
- [ ] **Set `rotate_mb` and a retention policy.** Both retention limits default to keeping
      everything forever, and the sweeper only runs when the dashboard is enabled. [The decision
      ledger on disk](/docs/operate/ledger-lifecycle)

<Callout type="key">
The two settings most often left at their defaults are the two that fill a disk: retention keeps
history forever unless you say otherwise, and it does not run at all on a deployment with
`dashboard.enabled: false`. Decide both, rather than inheriting them.
</Callout>

## Before you expose it

- [ ] **Terminate TLS in front of the data plane.** The binary terminates none, and virtual keys
      travel as bearer credentials. [Production hardening](/docs/operate/hardening)
- [ ] **Keep the admin plane unreachable from outside.** It is `ClusterIP` and cannot be
      overridden; reach it by port-forward. On Compose it binds loopback unless you widen it.
      [Production hardening](/docs/operate/hardening)
- [ ] **Mint a credential and use the lowest role that works.** Nothing exists until you mint;
      the plaintext is shown once. [Security & secrets](/docs/operate/security)
- [ ] **Supply provider keys by reference.** The config names an environment variable; nothing in
      the chart, the values or the release holds a value. [Security &
      secrets](/docs/operate/security)
- [ ] **Read back what the defaults rendered.** The security context, the mounts and the
      NetworkPolicy are the hardening you did not configure and so are the likeliest to surprise
      you. [Production hardening](/docs/operate/hardening)

## Before you trust the numbers

- [ ] **Validate the config the chart will actually mount**, not the values file you wrote. Helm
      cannot run the binary, so an invalid config installs cleanly and then crash-loops.
      [Configuration in production](/docs/operate/configuration)
- [ ] **Run `doctor` against the running instance.** It checks the config, the compile, the
      ledger and the read-model lag in one pass. [Diagnose a running
      instance](/docs/operate/diagnostics)
- [ ] **Confirm the CLI and the dashboard agree on the row count.** A disagreement means the
      read-model is missing your rotated history. [Diagnose a running
      instance](/docs/operate/diagnostics)
- [ ] **Scrape the metrics endpoint.** A dropped ledger row exists nowhere except a counter, so a
      deployment that scrapes nothing cannot learn its totals are short. [Scrape the metrics
      endpoint](/docs/build/observability-metrics)

## Before you leave it alone

- [ ] **Take a backup and list the archive.** A backup you have not run `tar tzf` over is a hope.
      [Back up and restore](/docs/operate/backup-restore)
- [ ] **Rehearse the restore.** The failure mode is discovering at 3am that the read-model came
      back holding a fraction of your history. [Back up and
      restore](/docs/operate/backup-restore)
- [ ] **Load the alert rules, and add one for free disk.** Three ship with the chart; none of them
      watches the volume. [Runbooks](/docs/operate/runbooks)
- [ ] **Know where the runbooks are.** Symptom first, one hop to a fix.
      [Runbooks](/docs/operate/runbooks)
- [ ] **Practise an upgrade and a rollback.** Both are one command and neither touches the
      ledger. That is worth knowing from experience rather than from this sentence. [Upgrade and
      roll back](/docs/operate/upgrades)

## If your environment adds constraints

Three situations change the list rather than extend it.

**No route to the internet.** The offline path is structural: zero chart dependencies, no base
image, a compiled-in price table. Every outbound tier is gated on a consent string you type. [Install air-gapped](/docs/operate/air-gap)

**More than one replica.** Governance coordinates across the fleet through a shared store; the
ledger does not, and each replica keeps its own on its own volume. Your storage arithmetic
multiplies by N. [Multi-replica & scale-out](/docs/operate/scale-out)

**A cluster that forbids `kubectl exec`.** The credential bootstrap has no alternative today.
That is a hard gap rather than an inconvenience. Read it before you commit. [Security &
secrets](/docs/operate/security)

<Details summary="What a first deploy looks like end to end">

The shortest path that satisfies the list above, on Kubernetes:

```console
$ kubectl create namespace tokentriage
$ kubectl create secret generic tt-provider-keys -n tokentriage \
    --from-literal=ANTHROPIC_API_KEY=<your key>
$ helm template tt deploy/helm/tokentriage -f my-values.yaml \
  | python -c "import sys,yaml;[print(d['data']['tokentriage.yaml']) for d in yaml.safe_load_all(sys.stdin) if d and d.get('kind')=='ConfigMap']" \
  | tokentriage validate --config -
$ helm install tt deploy/helm/tokentriage -n tokentriage -f my-values.yaml
$ kubectl rollout status statefulset/tt-tokentriage -n tokentriage
$ helm test tt -n tokentriage
$ kubectl exec -n tokentriage tt-tokentriage-0 -- \
    tokentriage token new --role admin --state-dir /data/.tokentriage/dash
```

Then read the post-install notes, which resolve every name above for your own release, and
point one application's base URL at the data plane before you point all of them.

</Details>

## What this list deliberately omits

Routing, governance and caching decisions are not on it. TokenTriage starts in `ledger` mode,
where it changes nothing about your requests, and that is the right rung to run in production
while you decide the rest. Climbing is a separate, evidence-gated exercise: [your first routing
rollout](/docs/get-started/first-routing-rollout) is the on-ramp, and
[governance](/docs/build/governance) owns spend limits.

<NextSteps items={[
  {title: "Deployment topologies", href: "/docs/operate/deployment", description: "The six shapes, what each is for, and how to choose between them."},
  {title: "The decision ledger on disk", href: "/docs/operate/ledger-lifecycle", description: "The growth, rotation and retention behind the first four items on this list."},
  {title: "Production hardening", href: "/docs/operate/hardening", description: "The exposure decisions, with the rendered defaults to check them against."},
  {title: "Runbooks", href: "/docs/operate/runbooks", description: "What to read when something on this list turns out to matter."}
]} />
