Search

↑↓ to move↵ to openesc to closetop 6 documentation results

Book a demoTry TokenTriage

In development · Explore the product preview. Join the launch list or book a walkthrough.

Browse docs

Deployment topologies

Six ways to run the same thing. What each shape is for, what it hands you by default, and which one to pick.

ExplanationLast updated

On this page

TokenTriage deploys as one static binary with one durable directory beside it. Everything below is a different wrapper around that fact, and none of them changes the topology: one process, one data volume, two listeners.

That is worth saying plainly because it sets expectations. There is no control plane to install, no database to provision, no queue, no sidecar. Choosing a deployment shape is choosing how you would rather manage a process, not choosing an architecture.

Choose one

Shape Reach for it when What you manage
the binary a host you already configure, or a laptop a service unit and a directory
a container an existing container host or PaaS one image, one volume, two ports
Docker Compose a single host you want durable and locked down a compose file and an env file
Helm Kubernetes — the canonical artifact a values file
Kustomize Kubernetes under GitOps that cannot run Helm at apply an overlay
Terraform a single VM on AWS with the storage and load balancer alongside a module invocation

The Helm chart is canonical in a specific sense: the Kustomize base is rendered from it, with a CI gate keeping the two identical, and the Compose overlays and the Terraform module express the same single-node shape through their own primitives. Pick the one that fits how you already run things.

What every shape gives you

Two listeners, always, and they are not interchangeable.

The data plane takes proxy traffic on 8787. It is what your applications point at, and it is the one you may expose once TLS terminates in front of it.

The admin plane serves 9090: the dashboard, the admin API, /metrics and /health. It carries ledger metadata and the config surface, and it is not for the public internet. On Kubernetes its Service type is a constant the chart will not let you override; on Compose it binds loopback until you widen it yourself.

One data directory holds everything durable: the ledger, the governance store, the auth stores and the derived index. /data in the images and on Kubernetes, ~/.tokentriage for a host binary.

Kubernetes, with Helm

One StatefulSet at every topology, where a single node is replicas: 1, with one image, zero chart dependencies, and one volume claim per pod.

$ helm install tt deploy/helm/tokentriage \
    --namespace tokentriage --create-namespace \
    -f my-values.yaml

The post-install notes resolve, for your release, how to reach both planes, the credential one-liner, and the storage disclosure. Reprint them any time with helm get notes tt -n tokentriage.

You supply two things: the config body under config:, passed to the binary verbatim, and a reference to a Secret holding your provider keys. The chart renders no Secret of its own.

config:
  mode: ledger
  upstreams:
    anthropic:
      base_url: https://api.anthropic.com
      dialect: anthropic
      api_key_env: ANTHROPIC_API_KEY
  ledger:
    decision_log:
      path: /data/.tokentriage/decisions.jsonl

providerSecrets:
  existingSecret: tt-provider-keys

Confirm the install with the bundled test, which asserts both halves of the secured-by-default state without needing a credential:

$ helm test tt -n tokentriage
TEST SUITE:     tt-tokentriage-test-connection
Phase:          Succeeded

Kubernetes, without Helm

deploy/k8s/base is a Kustomize base rendered from the chart, with airgap-mirror and prod-pinned overlays, for shops that cannot run Helm at apply time:

$ kubectl apply -k deploy/k8s/base

The overlay image digests are invalid all-zero placeholders. They fail to pull until you replace them from the release’s image list. That is a loud failure in preference to a silently mutable tag.

Docker Compose

Overlays layer onto the root compose file, and they compose with each other. Four are worth knowing:

Overlay What it adds
compose.prod.yaml a pinned image, a restart policy, resource limits, the env_file contract
compose.hardened.yaml read-only rootfs, all capabilities dropped, no-new-privileges, a tmpfs /tmp
compose.airgap.yaml a gateway-less internal network and an in-network mock upstream
compose.backends.yaml development-grade Redis and Postgres, each behind its own profile

Hardened production is the two stacked:

$ docker compose -f docker-compose.yml -f deploy/compose/compose.prod.yaml \
    -f deploy/compose/compose.hardened.yaml up -d

Each of the first three carries a test profile that gates on a real exit code rather than on up succeeding, because a proxy killed by a signal exits 0 and that must not read as a pass. All three were run for this page; the air-gap gate additionally asserts a costed row comes back with no route off the host.

Terraform on AWS

A single-VM example under deploy/terraform/aws/examples/single-vm builds on the tokentriage module: an EC2 instance, a dedicated EBS volume at /data, and a TLS-terminated load balancer in front of the data plane. The admin plane stays inside the security group and is reached through Session Manager.

Credentials are ARNs resolved on the instance into the canonical environment variable names, so no plaintext is passed to the module or written to Terraform state. A separate external-state example expresses the shared-state tier on RDS and ElastiCache; it is a preview root that CI validates and does not apply.

How the shapes differ on the things that bite
binary Compose Kubernetes
config file writable writable if you mount it so read-only projection
change config edit, or the admin API edit and restart edit values, helm upgrade
dashboard default off unless you enable it off in the baked config on, secured
admin bind 127.0.0.1 loopback publish 0.0.0.0, ClusterIP Service
credential bootstrap run the CLI docker compose exec kubectl exec
upgrade replace the binary pull and recreate helm upgrade, rolls the pod
roll back keep the old binary keep the old tag helm rollback

The read-only config on Kubernetes is the difference that surprises people most: configuration in production covers what still works there and what the dashboard refuses.

What runs the same everywhere

Three behaviours are properties of the binary, not of the wrapper, so they hold in all six shapes.

Each instance keeps its own ledger. There is no shared ledger file at any topology. A fleet view is a read-time merge over per-instance ledgers, typed by how much of the fleet it covered.

Nothing dials out unless you consent. External state, an L3 cache, an external embeddings API, OTLP export and the guardrail judge are each refused unless their config block carries external: "allow", a string no deployment layer types for you.

Prices come from a compiled-in table. There is no pricing service to reach, which is why an air-gapped install still produces costed rows.