Skip to main content
Tracelane is fully self-hostable. The same binary that powers gateway.tracelane.dev runs on a small VPS or a hardened production Docker Compose stack.

Prerequisites

Quick start — local development only

This section brings up a local development stack. It is not a production topology — see Production — two topologies below.
The fastest path to a running stack:
Then start the services:
The gateway listens on :8080, dashboard on :3000.

Production — two topologies

Pick one. They cannot be mixed: single-tenant self-host mode refuses to start if a Postgres, WorkOS or SPIRE setting is present in the environment (crates/shared/src/self_host.rs, MULTI_TENANT_SIGNAL_ENVS).

A. Single-tenant self-host — the shipped compose

infra/self-host/docker-compose.yml runs ClickHouse + NATS/JetStream + gateway + ingest on one box, built from source, as one workspace with no control plane: no Postgres, no WorkOS, no Polar, and no dashboard service. Every request authenticates with the bearer token TRACELANE_MASTER_KEY, and every span is stamped with TRACELANE_SINGLE_TENANT_ID. Provider keys are read from the environment (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY — set at least one).
Put a TLS-terminating reverse proxy in front of :8080. A self-hosted gateway with no control plane has no per-minute request cap (crates/gateway/src/rate_limiter.rs) — cap it at your reverse proxy if you need one.

B. Multi-tenant — running the hosted topology yourself

The hosted topology adds a Postgres control plane (tenants, API keys, entitlements, billing and the canonical audit ledger), WorkOS for identity, Polar for billing, and the Next.js dashboard. This repo ships no production compose file for it: infra/dev/docker-compose.yml brings up the backing services for development, and infra/docker/ holds the gateway and ingest Dockerfiles. Postgres is external (Neon or RDS); the dashboard runs on Vercel/Cloudflare; the gateway and ingest sit behind a TLS-terminating reverse proxy. (Tracelane Cloud’s own production compose is not published.) Images are built on-node from source. A published Helm chart and signed Chainguard/Wolfi image registry are on the roadmap — not yet shipped.

Environment variables — multi-tenant topology

The tables below are for topology B. Topology A’s variables are in its table above; do not add these to a single-tenant deployment.

Required (production)

Optional

There are no cold-tier storage variables for the gateway or ingest to read. Cold archival is a ClickHouse storage policy (an object-storage volume that aged parts move to), configured in ClickHouse itself — the hosted service runs one; a self-host without it keeps spans on the ClickHouse disk.

Apply migrations

SLO tables/views (slo_hourly_stats, v_slo_stats) live in infra/dev/clickhouse/migrations/06_genai_attr_keys_and_slo.sql, not in schema.sql — a fresh compose stack must also apply the migrations dir or /v1/slo returns UNKNOWN_TABLE.

Configure WorkOS

WorkOS handles SSO (Google, Microsoft, SAML, GitHub, email OTP).
  1. Create a WorkOS application at workos.com.
  2. Set the Redirect URI to https://app.<your-domain>/auth/callback.
  3. Configure Connections for the providers you want to support.
  4. Add a Webhook at https://gateway.<your-domain>/v1/webhooks/workos, subscribed to: organization.created, user.created, dsync.user.created.
  5. Copy the webhook secret to WORKOS_WEBHOOK_SECRET.
  6. Copy Client ID and API Key to WORKOS_CLIENT_ID and WORKOS_API_KEY.

Configure Polar

Tracelane bills through Polar.sh (Merchant of Record); there is no direct Stripe integration.
  1. Create Products with these lookup_key values (set on each product’s product_metadata) — one monthly base product per paid plan. Annual billing is not offered yet; when it is, it is a separate base-plus-usage pair per plan, described on the pricing page.
  2. Create six Meters with event_name:
    • ingest_gb, hot_gb_month, series, scan_units, cold_gb_month, eval_runs
  3. Create a Webhook at https://<your-dashboard-domain>/api/webhooks/polar, subscribed to: subscription.created, subscription.updated, subscription.active, subscription.canceled, order.created, order.paid.
  4. Issue an Organization Access Token with read/write scope for products, subscriptions, meter events, and customer-portal sessions.
  5. Set POLAR_ACCESS_TOKEN, POLAR_WEBHOOK_SECRET, and POLAR_EXPECTED_ORGANIZATION_ID.

Generate audit signing key

The SHA-256 hash chain and Ed25519 per-tenant signing are live. Anchored batches carry a resolved Sigstore Rekor v2 inclusion proof (best-effort, opt-in); universal per-run anchoring is on the roadmap.
openssl writes PKCS#8 v1 (seed only). Gateway builds before 2026-09-21 accept only the v2 form and refuse to boot on a v1 key with invalid Ed25519 PKCS#8 key: KeyRejected("VersionNotSupported"). On such a build, generate the key from the repository instead — it prints a v2 key:
Store rekor-signing.pem in your secrets manager. Losing it means you cannot anchor new audit batches — existing anchors remain verifiable.

Verify the stack

In topology A the bearer token is your TRACELANE_MASTER_KEY, not a tlane_… key — there is no dashboard to mint one.

Production checklist

Before accepting production traffic, in either topology:
  • TRACELANE_REKOR_SIGNING_KEY generated and stored; TRACELANE_REKOR_URL set if batches are to be anchored (unset, they are signed locally and never anchored)
  • ClickHouse pointing at production storage (not dev compose)
  • TLS termination at reverse proxy (Caddy, Nginx, or Cloudflare Tunnel)
  • OSV-Scanner + Grype clean on deployed commit
  • Conformance suites green: pnpm --filter @tracelanedev/audit-verifier test
Single-tenant self-host (topology A):
  • TRACELANE_MASTER_KEY set (32 random bytes, stored in secrets manager) — it is the bearer token every client sends
  • No POSTGRES_URL / PGHOST / WORKOS_CLIENT_ID / TRACELANE_SPIRE_SOCKET in the environment — the gateway and ingest refuse to start with one
Multi-tenant (topology B):
  • TRACELANE_BYOK_MASTER_KEY set and rotated (32 random bytes, stored in secrets manager)
  • Confirm POSTGRES_URL is set — a release build has no auth fallback and refuses every API key without it
  • Postgres pointing at production branch (Neon or RDS)
  • WorkOS webhook verified (send a test event, confirm tenant is provisioned)
  • Polar products + meters + webhook configured and verified

Upgrading