GitHubBook a demoStart routing
Browse docs

Security & secrets

No stored secrets — references only, keys hashed, TLS terminated in front, the air-gap consent gate, and the dashboard auth modes. The security posture that lets an audit pass in one read.

Last updated

On this page

TokenTriage’s security posture follows from two invariants: no stored secrets and no heuristic identity. The result is a system where a leaked config file leaks nothing, and every attribution is defensible.

Secrets are references, never values

  • Virtual keys and management tokens are stored as SHA-256 hashes, shown once at mint, and the key prefix is stripped before the upstream forward.
  • The Helm chart renders zero Secret objects. Provider keys flow via secretKeyRef to canonical env names; the plaintext exists only on your terminal, and only its hash is written to the pod’s PVC. The Terraform analog uses ARNs, never values; the Compose analog keeps the value in a git-ignored .env while the config names it.

TLS terminates in front

The data plane serves plaintext HTTP by design — terminate TLS in front of it (bearer virtual keys belong on TLS). The governance.keys.tls_terminated_upstream field is an operator attestation that TLS terminates in front (not a probe); running with bearer keys and no attestation is warned.

Every phone-out is gated. External state (Redis, Postgres, an L3 cache), an external embeddings API, and OTLP export are all refused unless their config block carries external: "allow". CI asserts the dialed-host set is a subset of the configured upstreams — so no deployment layer can silently un-gate the air-gap. The default posture dials nothing.

Dashboard auth

The dashboard supports three auth modes (dashboard.auth.mode):

  • off — requires a loopback admin bind (else it’s a validation error).
  • token — bearer tokens: tta_ management tokens (scoped, minted via the API), ttk_ bootstrap bearers (role-based, minted on the host with tokentriage token new), and tts_ share tokens (read-only, scoped views).
  • users — local users with argon2id password hashes (tokentriage user add), with a session cookie and a CSRF token bound to the session.

Content capture (the verbatim prompt/response bytes) is off by default (dashboard.capture.*); when enabled, it sits behind a dedicated privileged scope with one audit row per read attempt.

Everything is auditable

Probes report process-up only (GET /health). Errors are RFC 9457 application/problem+json with a request_id. The audit trail is append-only — one row per attempted mutation and per denied request, carrying the actor, the action, a content hash of any diff, and the config generation. A credential that appeared in a request path is redacted before the row is written.