Local setup & config · TrustedRouter Field Manual
Day 1

Local setup & config

Verified Day-1 setup on macOS plus the full environment variable reference.

Video 03 Day-1 setup · uv sync, tests, running the app, smoke tests, conformance containers.
Video 04 Config & environment · Defaults, operator keys, rollout.sh ENV_VARS, Secret Manager.

Every command below was run on a real Mac by the research agent on 2026-10-19 against main @ 778120494: macOS 26 (Darwin 25.6.0) on Apple Silicon, uv 0.11.6, Node v24.14.1, Docker CLI 29.6.1. ✅ = ran and passed exactly as written. ⚠️ = problem found; details are given next to it. The repo stayed clean (git status empty) after every step.


0. TL;DR (copy-paste, ~2 minutes on a warm machine)

cd quill-router
uv sync                                   # ✅ ~10 s (238 packages resolved)
uv run ruff check . && uv run mypy        # ✅ ~0.5 s + ~8–10 s
uv run pytest -q -x tests/conformance -k memory   # ✅ fast memory-backend contract
uv run uvicorn trusted_router.main:app --port 18080 --reload   # ✅ starts in about 1 s

You do not need a .env, cloud credentials, Docker, or provider keys to run the app or most of the test suite. The defaults are local-safe: TR_ENVIRONMENT=local, TR_STORAGE_BACKEND=memory and TR_ENABLE_LIVE_PROVIDERS=false, so providers are mocked.


1. Prerequisites

Tool Version Why Verified here
Python 3.12 (.python-version; requires-python >=3.11; mypy targets 3.12; Docker image is python3.12-bookworm-slim) Runtime ✅ uv auto-provisioned CPython 3.12.13
uv any recent version (CI uses astral-sh/setup-uv@v7 with version: latest) Package/venv manager. Always run things through uv run. ✅ 0.11.6
Node + npm CI pins Node 20 (actions/setup-node, node-version: "20") ESLint, Stylelint, tsc (dashboard TS → committed JS), Playwright ✅ Node 24.14.1 also works
Playwright Chromium @playwright/test ^1.62.1 Browser smoke tests (tests/browser/, tests-e2e/) ✅ npx playwright install chromium (~95 MiB, about 12 s)
Docker any Only needed for the Postgres / pgadapter / Spanner-emulator conformance backends ⚠️ CLI is installed, but the daemon was not running on this Mac (see Troubleshooting T1). Not needed for anything else.
gcloud / AWS / Azure CLIs — Not needed for local dev. Deploys go through CI (see 04-infra-deploy). n/a

Install hints (macOS):

curl -LsSf https://astral.sh/uv/install.sh | sh     # uv
brew install node@20   # or nvm install 20
# Docker Desktop / OrbStack / colima: only if you will run postgres/spanner conformance

2. Python environment

uv sync

✅ Result: Resolved 238 packages, installed the dev group (the default) into .venv/. Time: 9.8 s wall-clock with a warm uv cache. A second run takes 19 ms (Checked 214 packages).

Notes * The dev dependency group includes docling and sec-parser, which pull a large ML stack (torch, transformers). A cold-cache first sync therefore downloads a lot more than the production image needs. Production uses uv sync --frozen --no-dev. * CI runs uv sync --frozen. Use --frozen too if you want to be sure you are not silently changing uv.lock. * Do not pip install into the venv by hand. uv run re-syncs to the lockfile.


3. The three CI gates (from AGENTS.md: "run all three before claiming done")

uv run ruff check . && uv run mypy && uv run pytest -q
Gate Command Result on this Mac Time
Lint uv run ruff check . ✅ All checks passed! 0.55 s
Types uv run mypy ✅ Success: no issues found in 533 source files 9.6 s (warm) / 8.3 s with a cold --cache-dir. mypy is not slow here, because of follow_imports = "silent" and files = ["src/trusted_router"].
Tests uv run pytest -q Not run in full on purpose (multi-hour on one machine). Collection alone: 38,221 / 40,067 tests collected (1,846 deselected) in 29 s —

Rules worth tattooing (AGENTS.md): * Never run ruff format .. CI only runs ruff check, and formatting produces a huge unrelated diff. * Coverage must stay ≥ 70 %. CI shards the suite with pytest-split and runs -n 4 --dist loadgroup. * The default addopts deselects proof_oracle (over an hour of differentials) and provider_health (live provider state). If you touch async settle or billing, run the oracle explicitly: uv run pytest -q -n 4 -m proof_oracle tests/test_async_settle_proof_oracle.py. * CI's lint job also runs uv run python scripts/update_model_changes.py --check. ✅ It exited 0 here.

3a. Fast, targeted pytest runs (what to use during the inner loop)

# Storage contract against the in-memory backend (postgres/spanner params auto-skip without DSNs)
uv run pytest -q -p no:cacheprovider -x \
  tests/conformance/test_store_semantics.py tests/conformance/test_key_management.py -k memory

✅ 90 passed, 14 skipped, 416 deselected, 6 warnings in 1.07s (10 s wall-clock including uv and imports)

uv run pytest -q -p no:cacheprovider tests/test_billing.py tests/test_inference_errors.py

✅ 82 passed, 142 warnings in 7.72s

uv run pytest --collect-only -q        # count / sanity-check collection

✅ 29 s

Tips * -n 4 (xdist) is available for bigger subsets. * tests/conftest.py forces TR_STORAGE_BACKEND=memory, and the operator keys file is ignored under pytest unless TR_ALLOW_LOCAL_KEY_FILE_IN_TESTS=1. Tests are hermetic by default. * Pollution trap (issue #333): never monkeypatch.setattr(STORE, ...). STORE is a __getattr__ proxy. Patch the backend class instead. Run the full suite before you merge. * The warnings you will see are FastAPI on_event deprecation notices. They are harmless.


4. Run the app locally and exercise it end-to-end

uv run uvicorn trusted_router.main:app --port 18080          # add --reload while editing

✅ Log output:

INFO:     Started server process [9685]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:18080 (Press CTRL+C to quit)

Ready in under 1 s. (Production instead runs python -m trusted_router.serve, which reads PORT, WEB_CONCURRENCY/UVICORN_WORKERS, UVICORN_PROXY_HEADERS, UVICORN_FORWARDED_ALLOW_IPS and UVICORN_TIMEOUT_KEEP_ALIVE, and keeps the listener open briefly after SIGTERM.)

4a. Health and catalog ✅

curl -s localhost:18080/health            # {"status":"ok"}   200, ~2 ms
curl -s localhost:18080/v1/health         # {"status":"ok"}   200
curl -s localhost:18080/v1/models/count   # {"data":{"count":692}}
curl -s localhost:18080/v1/models | jq '.data[:5][].id'
# "trustedrouter/auto" "trustedrouter/free" "trustedrouter/cheap" "trustedrouter/fast" "trustedrouter/green"

/, /docs, /status and /openapi.json all return 200. /healthz is not a route. It returns a markdown-flavoured 404 that points agents at llms.txt and sitemap.xml.

4b. Local auth: no OAuth needed ✅

In local/test you can act as any user by sending the x-trustedrouter-user header (this is what scripts/smoke_e2e.py and the tests do). The first call auto-creates a "Personal Workspace".

B=http://127.0.0.1:18080/v1
H='x-trustedrouter-user: dev@example.com'
curl -s $B/workspaces -H "$H"
KEY=$(curl -s -X POST $B/keys -H "$H" -H 'content-type: application/json' \
      -d '{"name":"local dev"}' | jq -r .key)          # sk-tr-v1-...

4c. Fund the workspace, then chat ✅ (the non-obvious bit)

A brand-new local workspace has $0, so the first chat returns 402 {"error":{"code":402,"message":"Insufficient credits","type":"insufficient_credits"}}. Trial credit is only granted on the OAuth/email signup paths. With no TR_STRIPE_SECRET_KEY, /billing/checkout returns a "mode":"mock" session, and in local/test the Stripe webhook accepts unsigned JSON. So you can credit yourself like this:

WS=$(curl -s $B/workspaces -H "$H" | jq -r '.data[0].id')
curl -s -X POST $B/internal/stripe/webhook -H 'content-type: application/json' -d "{
  \"id\":\"evt_local_1\",\"type\":\"checkout.session.completed\",
  \"data\":{\"object\":{\"amount_total\":2500,\"payment_intent\":\"pi_local_1\",
  \"payment_status\":\"paid\",\"metadata\":{\"workspace_id\":\"$WS\"}}}}"
# {"data":{"credited":true,"credited_microdollars":25000000,"event_id":"evt_local_1",...}}
curl -s $B/credits -H "$H"     # available: 25.0

(The event is idempotent by id. Change evt_local_N to top up again.)

curl -s $B/chat/completions -H "authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{"model":"trustedrouter/auto","messages":[{"role":"user","content":"hello"}]}'

✅ Response (trimmed):

{"object":"chat.completion","model":"z-ai/glm-5.3-flash",
 "choices":[{"message":{"role":"assistant","content":"TrustedRouter response from z-ai/glm-5.3-flash."},"finish_reason":"stop"}],
 "usage":{"prompt_tokens":1,"completion_tokens":11,"total_tokens":12},
 "trustedrouter":{"content_stored":false,"requested_model":"trustedrouter/auto","selected_model":"z-ai/glm-5.3-flash","rollover_failures":[]}}

Streaming ("stream":true) ✅ emits an event: trustedrouter.route SSE frame and then standard chat.completion.chunk deltas. GET /v1/activity?group_by=none ✅ shows the metadata-only row (cost_microdollars: 3, no prompt content).

How the fake provider works: services/inference.py builds ProviderClient(LocalKeyFile(settings.local_keys_file), live=settings.enable_live_providers). With live=False (the default, and also in production, where real inference happens in the attested gateway, quill-cloud-proxy), providers.py returns mock_text(model.id) → "TrustedRouter response from <model>." No network calls are made. Set TR_ENABLE_LIVE_PROVIDERS=true and put real keys in the keys file (section 6) to call real upstreams from your laptop.

Other observations: * No auth → 401 Missing Authentication header ✅ * cerebras/llama3.1-8b → 400 model_not_supported. That model is no longer chat-capable in the catalog (see T3).

Stop the server with Ctrl-C (or pkill -f "uvicorn trusted_router.main:app").

4d. scripts/smoke_e2e.py ⚠️ stale

TR_SMOKE_BASE_URL=http://127.0.0.1:18080/v1 uv run python scripts/smoke_e2e.py

⚠️ E2E smoke failed: expected 54 OpenRouter route classifications, got 95. The script (last touched 2026-05-04) hard-codes 54 coverage rows and uses cerebras/llama3.1-8b. It also assumes new workspaces have credit. Treat it as reference reading, not a working check. Use the curl flow above instead.


5. Frontend / npm

Root package.json (trusted-router-frontend). The dashboard TypeScript in frontend/src/ compiles into committed JS under src/trusted_router/static/.

Script Command What it does Verified
install npm ci 213 packages ✅ 0.6 s
lint npm run lint → eslint frontend/src TS lint ✅ clean, 0.8 s
lint:css npm run lint:css → stylelint src/trusted_router/static/*.css CSS lint ✅ clean, 0.6 s
typecheck npm run typecheck → tsc --noEmit Type check only ✅ clean, 0.5 s
build npm run build → tsc Writes src/trusted_router/static/*.js. CI runs npx tsc and then git diff --exit-code -- src/trusted_router/static/dashboard.js src/trusted_router/static/token-index.js. If you edit .ts, commit the regenerated .js. ✅ the output matched the committed JS (no diff)
test:browser npm run test:browser → playwright test (playwright.config.js, testDir: tests/browser) Homepage/console/chat smoke. Its webServer starts uvicorn on :18081 with TR_ENVIRONMENT=test TR_STORAGE_BACKEND=memory TR_RATE_LIMIT_ENABLED=false and fake OAuth client IDs ✅ npx playwright test tests/browser/homepage.spec.js --project=chromium → 9 passed (5.4 s)
build:world-map node scripts/build-world-svg.mjs Regenerates the world SVG asset not run (it writes tracked files)

First-time browser install: npx playwright install chromium ✅ (CI uses --with-deps chromium).

tests-e2e/ is a separate npm project (its own package.json and playwright.config.ts) for the /chat playground, with 19 specs. It mocks /v1/chat/completions via page.route():

cd tests-e2e && npm install && npm run install:browsers   # chromium webkit firefox
npm test -- --project=chromium      # also: test:headed, test:debug, test:ui

(Not run by this agent. The README says the server auto-starts via webServer.)


6. Configuration: how settings are resolved

All runtime config lives in one Pydantic Settings class: src/trusted_router/config.py::Settings (2,388 lines, 354 fields). It is built by get_settings() (no caching side effects).

  • env_prefix="TR_": field storage_backend ↔ env TR_STORAGE_BACKEND.
  • env_file=".env" (relative to the CWD, gitignored). .env.example is the template.
  • extra="ignore": unknown TR_* vars are silently ignored, so a typo does nothing.
  • hide_input_in_errors=True: validation errors don't echo secret values.
  • Validators fail closed. For example, TR_ENVIRONMENT=production alone refuses to start: TR_SERVICE_SURFACE=combined is restricted to local/test unless the temporary TR_ALLOW_DEPLOYED_COMBINED_SURFACE=true migration bridge is explicitly enabled ✅ (verified). TR_REMEDIATOR_MODE must be exactly off|observe|act.

Source priority (highest wins), from Settings.settings_customise_sources:

  1. init kwargs (tests)
  2. process environment (TR_*)
  3. .env in the CWD
  4. operator keys file ~/.quill_cloud_keys.private (_LocalKeyFileSource). Only for the 53 secret/integration fields listed in _LOCAL_KEY_FALLBACKS, looked up by the un-prefixed upper-case name (e.g. STRIPE_SECRET_KEY, GOOGLE_CLIENT_ID, SENTRY_DSN). Skipped entirely under pytest.
  5. file secrets (pydantic default; unused)
  6. code defaults in config.py

The operator keys file

  • Dotenv-style, never committed. Parsed by secrets.LocalKeyFile (KEY=value, # comments, quotes stripped). Values are never logged.
  • README documents /Users/jperla/claude/.quill_cloud_keys.private. That is Joseph's path. For you it is ~/.quill_cloud_keys.private (the code default). It does not exist on this Mac, and nothing needs it.
  • Two consumers: 1. Settings fallback (above): integration secrets such as Stripe, OAuth, SES, PayPal, Adyen, Sentry, INTERNAL_GATEWAY_TOKEN and BOOTSTRAP_MANAGEMENT_KEY. 2. Provider client (providers.py::_secret): provider keys such as ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, CEREBRAS_API_KEY, DEEPSEEK_API_KEY and MISTRAL_API_KEY. It checks the keys file first and then the bare process env. These are not TR_-prefixed.
  • Aliases (secrets.KEY_ALIASES): CLAUDE_API_KEY→Anthropic, CHATGPT_API_KEY→OpenAI, STRIPE_KEY→STRIPE_SECRET_KEY, ZHIPU_API_KEY/Z_AI_API_KEY→ZAI_API_KEY, TOGETHERAI_API_KEY→Together, DASHSCOPE_API_KEY→Alibaba, and Vertex project/region/token aliases.
  • Vertex in production uses the service account and ADC, not a long-lived key in this file.
  • ⚠️ Gotcha (code-verified): TR_LOCAL_KEYS_FILE changes the path the provider client reads, but _LocalKeyFileSource reads settings_cls.model_fields["local_keys_file"].default, which is always ~/.quill_cloud_keys.private. So the Settings fallback for Stripe, OAuth and the rest ignores TR_LOCAL_KEYS_FILE. .env.example sets it to Joseph's path. If you copy .env.example verbatim, provider keys are read from a non-existent file, while integration secrets still come from ~/.quill_cloud_keys.private.

Production: config-as-code

  • Runtime env for Cloud Run is the ENV_VARS array in scripts/deploy/rollout.sh (~120 TR_* entries; joined with ^|^ and passed as --set-env-vars). Setting a Cloud Run env var by hand is a no-op. The next rollout overwrites it. To change prod config, edit rollout.sh in a PR.
  • Notable prod values: TR_ENVIRONMENT=production, TR_SERVICE_SURFACE=combined with TR_ALLOW_DEPLOYED_COMBINED_SURFACE (temporary bridge until #712), TR_RATE_LIMIT_ENABLED=false (no trusted edge client identity yet), TR_ENABLE_LIVE_PROVIDERS=false, TR_REMEDIATOR_IN_PROCESS_ENABLED=false, TR_SIGNUP_TRIAL_CREDIT_MICRODOLLARS=300000, and TR_STORAGE_BACKEND=${STORAGE_BACKEND} (spanner-clickhouse per README).
  • Secrets come from GCP Secret Manager via --update-secrets:
  • Mandatory SECRET_ENVS: TR_SENTRY_DSN, TR_STRIPE_SECRET_KEY, TR_STRIPE_WEBHOOK_SECRET, TR_INTERNAL_GATEWAY_TOKEN, and the async-settle Ed25519 PEM mounted as a file at /secrets/async-settle/ticket.pem. A missing one fails the deploy.
  • About 67 optional ones, added through add_secret_env_if_exists only when the secret exists in the inventory; otherwise they are explicitly --remove-secrets'd. These include provider keys (ANTHROPIC_API_KEY … ZERO_G_API_KEY), OAuth client ID/secret, Twilio/Telnyx/Veriff, PayPal/Routable/Adyen, AWS keys, AXIOM_API_TOKEN and the federation/operator tokens.
  • Non-GCP deployments (AWS/Azure) have their own workflows (deploy-aws-control-plane.yml, deploy-azure-control-plane.yml) and use Workload Identity Federation (TR_GOOGLE_EXTERNAL_CREDENTIAL_CONFIG in scripts/entrypoint.sh, which refuses key material). See 04-infra-deploy.
flowchart TD
    D["Code defaults<br/>src/trusted_router/config.py::Settings<br/>(354 fields, local-safe)"]
    K["Operator keys file<br/>~/.quill_cloud_keys.private<br/>(un-prefixed names; 53 secret fields only;<br/>skipped under pytest)"]
    E[".env in CWD<br/>(gitignored; template: .env.example)"]
    P["Process env TR_*<br/>(shell, playwright webServer, CI)"]
    I["Init kwargs<br/>(tests: Settings(...))"]
    S(["Settings instance<br/>get_settings()"])
    D --> K --> E --> P --> I --> S

    subgraph PROD["Production (Cloud Run, GCP)"]
      R["scripts/deploy/rollout.sh<br/>ENV_VARS array → --set-env-vars<br/>(config-as-code; manual edits overwritten)"]
      SM["GCP Secret Manager<br/>SECRET_ENVS + add_secret_env_if_exists<br/>→ --update-secrets (env vars + PEM file mount)"]
      R --> P2["Container env TR_* + provider keys"]
      SM --> P2
    end
    P2 -. same precedence slot as .-> P

    PK["Provider API keys<br/>ANTHROPIC_API_KEY, OPENAI_API_KEY ...<br/>(keys file first, then bare env)"] --> PC(["ProviderClient<br/>live only if TR_ENABLE_LIVE_PROVIDERS=true"])
    K -. provider keys .-> PK
Diagram source
flowchart TD
    D["Code defaults<br/>src/trusted_router/config.py::Settings<br/>(354 fields, local-safe)"]
    K["Operator keys file<br/>~/.quill_cloud_keys.private<br/>(un-prefixed names; 53 secret fields only;<br/>skipped under pytest)"]
    E[".env in CWD<br/>(gitignored; template: .env.example)"]
    P["Process env TR_*<br/>(shell, playwright webServer, CI)"]
    I["Init kwargs<br/>(tests: Settings(...))"]
    S(["Settings instance<br/>get_settings()"])
    D --> K --> E --> P --> I --> S

    subgraph PROD["Production (Cloud Run, GCP)"]
      R["scripts/deploy/rollout.sh<br/>ENV_VARS array → --set-env-vars<br/>(config-as-code; manual edits overwritten)"]
      SM["GCP Secret Manager<br/>SECRET_ENVS + add_secret_env_if_exists<br/>→ --update-secrets (env vars + PEM file mount)"]
      R --> P2["Container env TR_* + provider keys"]
      SM --> P2
    end
    P2 -. same precedence slot as .-> P

    PK["Provider API keys<br/>ANTHROPIC_API_KEY, OPENAI_API_KEY ...<br/>(keys file first, then bare env)"] --> PC(["ProviderClient<br/>live only if TR_ENABLE_LIVE_PROVIDERS=true"])
    K -. provider keys .-> PK

Read the top chain as "lowest priority → highest priority". Each later source overrides the one before it.


7. Env var reference

7a. The ones you actually care about locally

Env var Default Purpose Required locally?
TR_ENVIRONMENT local local/test unlock dev affordances: the x-trustedrouter-user header, the unsigned Stripe webhook, mock checkout, combined surface. Anything else is treated as deployed and fails closed. No (default is right)
TR_STORAGE_BACKEND memory memory (in-process and lost on restart), postgres, spanner/spanner-clickhouse (prod) No
TR_ENABLE_LIVE_PROVIDERS false true makes real upstream calls using the keys file / env No (true only to hit real models)
TR_LOCAL_KEYS_FILE ~/.quill_cloud_keys.private Path for provider keys (see the gotcha above) No
TR_SERVICE_SURFACE combined Process role: combined\|public\|actions\|control\|internal\|observer No
TR_RATE_LIMIT_ENABLED true Per-IP (240/min), per-key (1200/min) and internal (6000/min) limits. Turn it off for load or browser tests (Playwright does). No
TR_RATE_LIMIT_IP_PER_WINDOW / _KEY_ / _INTERNAL_ / _WINDOW_SECONDS 240 / 1200 / 6000 / 60 Limits No
TR_POSTGRES_DSN "" Postgres-wire system of record (Azure, AWS DSQL, Spanner-PG) Only with postgres backend
TR_GCP_PROJECT_ID / TR_SPANNER_INSTANCE_ID / TR_SPANNER_DATABASE_ID quill-cloud-proxy / None / None Spanner target Only with spanner backends (never point at prod)
TR_INTERNAL_GATEWAY_TOKEN None Protects /v1/internal/gateway/* (authorize/settle/refund) No. Required outside local.
TR_BOOTSTRAP_MANAGEMENT_KEY None Seeds a management API key at startup No
TR_STRIPE_SECRET_KEY / TR_STRIPE_WEBHOOK_SECRET None Real Stripe. Unset means mock checkout and an unsigned local webhook. No
TR_GOOGLE_CLIENT_ID/_SECRET, TR_GITHUB_CLIENT_ID/_SECRET None OAuth sign-in. Each is independently optional. No (use the header instead)
TR_SENTRY_DSN None Error reporting. Disabled in local unless TR_SENTRY_LOCAL_ENABLED. No
TR_SIGNUP_TRIAL_CREDIT_MICRODOLLARS 300000 Starter credit on OAuth/email signup No
TR_EMAIL_SIGNUP_ENABLED false Plain-email POST /v1/signup (credit-farming vector, so off by default) No
TR_ALLOW_LOCAL_KEY_FILE_IN_TESTS unset 1 lets pytest read the keys file No
TR_CONFORMANCE_POSTGRES_DSN unset Enables the postgres conformance params Only for conformance
TR_CONFORMANCE_SPANNER_PG_DSN unset Enables the spanner-pg conformance params Only for conformance
TR_SMOKE_BASE_URL, TR_SMOKE_USER, TR_SMOKE_INTERNAL_TOKEN http://127.0.0.1:18080/v1, smoke@example.com, — scripts/smoke_e2e.py inputs No
PORT, WEB_CONCURRENCY/UVICORN_WORKERS, UVICORN_* — trusted_router.serve (container entrypoint) No (use uvicorn directly)
Provider keys ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, … — Upstream auth (no TR_ prefix) Only with live providers

.env.example (18 lines) contents, annotated: TR_ENVIRONMENT=local, TR_ENABLE_LIVE_PROVIDERS=false, TR_LOCAL_KEYS_FILE=/Users/jperla/... (⚠️ change it or drop it), TR_STORAGE_BACKEND=memory, and Spanner/Bigtable IDs (inert with memory; TR_BIGTABLE_* are not Settings fields any more, so extra="ignore" drops them). The rest are empty secrets plus the rate-limit numbers. ✅ Loading it verbatim gives local memory False … True combined and import trusted_router.main succeeds.

7b. Direct os.environ reads outside Settings

Most are in ops scripts, the synthetic monitor and deploy tooling, not in the request path: * Storage bootstrap scripts: TR_STORAGE_BACKEND, TR_GCP_PROJECT_ID, TR_SPANNER_INSTANCE_ID, TR_SPANNER_DATABASE_ID (20+ reads each in scripts/), TR_POSTGRES_DSN, TR_SPANNER_POOL_SIZE, TR_SCHEMA_*. * Synthetic monitor (synthetic/cli.py, image_generation.py, video_generation.py): TR_SYNTHETIC_*, TR_MONITOR_API_KEY, TR_EMBEDDINGS_*. * Deploy mutex/outcome: TR_DEPLOY_*. Smoke: TR_SMOKE_*, TR_API_BASE, TR_API_KEY. * serve.py: PORT, WEB_CONCURRENCY, UVICORN_*. entrypoint.sh: TR_GOOGLE_EXTERNAL_CREDENTIAL_CONFIG. * Provider keys via providers.py::_secret (keys file, then env), plus DATABRICKS_HOST/TOKEN, VERTEX_PROJECT_ID, etc. * CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE, GITHUB_TOKEN and GITHUB_STEP_SUMMARY in CI/ops scripts.

7c. Full Settings reference (all 354 fields, auto-generated)

Generated by introspecting Settings.model_fields and taking the first sentence of the comment directly above each field in config.py. Secret-looking fields never show values: they read None/"" or "(non-empty code default)". "Keys-file fallback = yes" means the field can come from ~/.quill_cloud_keys.private under its un-prefixed upper-case name. Grouping is by name prefix, so it is approximate. Required for local: none of them. Every field has a default, and the defaults target local + memory. Production requirements are enforced by the validators (production_is_fail_closed, operational_analytics_problems, …) and supplied by rollout.sh + Secret Manager.

Core runtime & surfaces (37)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_ENVIRONMENT "local"
TR_RELEASE "local"
TR_SOURCE_REVISION ""
TR_SERVICE_NAME "trusted-router"
TR_SERVICE_SURFACE "combined" One image serves several deliberately disjoint process roles.
TR_ALLOW_DEPLOYED_COMBINED_SURFACE False Temporary compatibility bridge for the pre-split GCP service.
TR_API_BASE_URL "https://api.trustedrouter.com/v1"
TR_TRUSTED_DOMAIN "trustedrouter.com"
TR_TRUSTED_DOMAIN_ALIASES "allyrouter.com,uptimerouter.com" Additional first-party control-plane domains.
TR_SECURITY_CONTACT_EMAIL "security@trustedrouter.com"
TR_SUPPORT_EMAIL "help@trustedrouter.com"
TR_ENABLE_LIVE_PROVIDERS False
TR_LOCAL_KEYS_FILE "~/.quill_cloud_keys.private"
TR_PUBLIC_CLIENT_OBSERVED_ENABLED False Client-observed figures include upstream provider-caused failures and can understate a healthy gateway.
TR_SIGNUP_TRIAL_CREDIT_MICRODOLLARS 300000 Starter credit granted exactly once with a new email/social OAuth account's first workspace.
TR_SIWE_DOMAIN None MetaMask uses public-key crypto (no shared secret). yes
TR_RETIREMENT_NOTICES_MODE "off" Daily route-retirement notices.
TR_RETIREMENT_NOTICES_PREVIEW_EMAIL None
TR_STABLECOIN_CHECKOUT_ENABLED True
TR_USER_MODELS_DISPATCH_ENABLED False Whether the gateway will AUTHORIZE requests against user-provided models.
TR_SPECULATIVE_PROVIDER_SHADOW_ENABLED False PR3 observes only;
TR_SPEND_LEASE_ACCEPTED_GCP_IMAGE_DIGESTS "" Audited break-glass addition to the signed Stage D runtime policy.
TR_SPEND_LEASE_TRUST_ELIGIBILITY_ENABLED False Arms the authorize-time billing-pause gate (trust_eligibility .billing_paused_tx).
TR_MAX_WORKSPACES_PER_OWNER 25
TR_OPERATOR_TOKEN ""
TR_OPERATOR_IDENTITIES ""
TR_REAP_SNAPSHOT_BOOKING_ENABLED False Decision 70's billing change.
TR_READ_ONLY False Operational read-only flag.
TR_REGIONS "us-central1,us-east4,europe-west4,us-west1" Attested gateway regions.
TR_PRIMARY_REGION "us-central1"
TR_SERVING_REGION "" TR_SERVING_REGION identifies this process's deployment, not the primary Spanner/journal region.
TR_REGIONAL_API_HOSTNAME_TEMPLATE "api-{region}.quillrouter.com"
TR_USER_CREDIT_TRANSFER_DAILY_CAP_MICRODOLLARS 100000000 Same-plane user-to-user transfers are final.
TR_MAX_REQUEST_BODY_BYTES 4194304
TR_MAX_IN_FLIGHT_REQUEST_BODY_BYTES 67108864
TR_MAX_CONCURRENT_REQUEST_BODIES 16
TR_REQUEST_BODY_READ_TIMEOUT_SECONDS 30.0

Storage backends (Spanner/Postgres/ClickHouse) (25)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_STORAGE_BACKEND "memory"
TR_POSTGRES_DSN "" Postgres-wire system of record for the non-GCP deployments.
TR_POSTGRES_IAM_AUTH "" Aurora DSQL passwords are short-lived IAM tokens.
TR_POSTGRES_IAM_REGION "" Normally inferred from <id>.dsql.<region>.on.aws.
TR_GCP_PROJECT_ID "quill-cloud-proxy"
TR_SPANNER_INSTANCE_ID None
TR_SPANNER_DATABASE_ID None
TR_GENERATION_RECORDS_ENABLED False
TR_CLICKHOUSE_URL "" Legacy in-process ClickHouse mirror.
TR_CLICKHOUSE_USER ""
TR_CLICKHOUSE_PASSWORD ""
TR_CLICKHOUSE_BENCHMARK_TABLE "provider_benchmark_samples"
TR_PROVIDER_ANALYTICS_CLICKHOUSE_URL "" Private, read-only provider portal connection.
TR_PROVIDER_ANALYTICS_CLICKHOUSE_USER "tr_provider_read"
TR_PROVIDER_ANALYTICS_CLICKHOUSE_PASSWORD ""
TR_PROVIDER_ANALYTICS_CLICKHOUSE_DATABASE "tr"
TR_PROVIDER_ANALYTICS_CLICKHOUSE_TABLE "provider_benchmark_samples"
TR_OPERATIONAL_ANALYTICS_CLICKHOUSE_URL ""
TR_OPERATIONAL_ANALYTICS_CLICKHOUSE_USER "tr_control_read"
TR_OPERATIONAL_ANALYTICS_CLICKHOUSE_PASSWORD ""
TR_OPERATIONAL_ANALYTICS_CLICKHOUSE_DATABASE "tr"
TR_OPERATIONAL_ANALYTICS_OUTBOX_ENABLED False Durable tenant-activity and synthetic-status stream.
TR_OPERATIONAL_ANALYTICS_SINK "outbox" HOW operational telemetry leaves the process.
TR_OPERATIONAL_ANALYTICS_CLICKHOUSE_WRITE_USER "tr"
TR_OPERATIONAL_ANALYTICS_CLICKHOUSE_WRITE_PASSWORD ""

Security, auth & OAuth (49)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_SOCIAL_REWARDS_ENABLED False Enabled explicitly in the primary deployment configuration.
TR_SOCIAL_REWARDS_PLANE_ID ""
TR_SOCIAL_REWARDS_ISSUER_ID "gcp-primary"
TR_SOCIAL_REWARDS_YOUTUBE_URL "" YouTube stays hidden and unclaimable until an official channel is configured.
TR_SOCIAL_REWARDS_CLAIMS_PER_MINUTE 30 Shared account request throttle;
TR_GOOGLE_DATA_MANAGER_ENABLED False Metadata-only Google Ads conversion delivery.
TR_GOOGLE_DATA_MANAGER_ACCOUNT_ID None
TR_GOOGLE_DATA_MANAGER_LOGIN_ACCOUNT_ID None
TR_GOOGLE_DATA_MANAGER_SIGNUP_ACTION_ID None
TR_GOOGLE_DATA_MANAGER_ACTIVATED_ACTION_ID None
TR_GOOGLE_DATA_MANAGER_PURCHASE_ACTION_ID None
TR_GOOGLE_DATA_MANAGER_KMS_KEY_NAME None
TR_GOOGLE_DATA_MANAGER_BATCH_SIZE 500
TR_GOOGLE_DATA_MANAGER_LEASE_SECONDS 300
TR_GOOGLE_DATA_MANAGER_MAX_ATTEMPTS 20
TR_GOOGLE_DATA_MANAGER_TIMEOUT_SECONDS 20.0
TR_GOOGLE_DATA_MANAGER_REPAIR_LOOKBACK_DAYS 90
TR_GOOGLE_DATA_MANAGER_STATUS_POLL_ATTEMPTS 6
TR_GOOGLE_DATA_MANAGER_STATUS_POLL_SECONDS 2.0
TR_ATTRIBUTION_COOKIE_KEY None Split public/control services may hold the base64-encoded, already-derived cookie key without receiving the legacy root that also authori… yes
TR_ATTRIBUTION_COOKIE_SECRET None yes
TR_INTERNAL_GATEWAY_TOKEN None yes
TR_BOOTSTRAP_MANAGEMENT_KEY None yes
TR_BYOK_KMS_KEY_NAME None yes
TR_BYOK_ENVELOPE_KEY_B64 None yes
TR_BYOK_ENVELOPE_KEY_REF "trustedrouter/byok-envelope-key/v1" yes
TR_BYOK_REGISTRATION_ENABLED True BYOK key REGISTRATION wraps the secret with the byok-envelope KMS key.
TR_GOOGLE_CLIENT_ID None OAuth providers — independently optional. yes
TR_GOOGLE_CLIENT_SECRET None yes
TR_GOOGLE_OAUTH_REDIRECT_URL None yes
TR_GOOGLE_OAUTH_LOGIN_AVAILABLE None The public service must render login links without receiving the OAuth client secrets owned by the control service.
TR_GOOGLE_ALIAS_CREDENTIALS_JSON "{}" Backup domains use independent provider credentials so login remains available even if the canonical domain or its OAuth app is unavailable. yes
TR_GITHUB_CLIENT_ID None yes
TR_GITHUB_CLIENT_SECRET None yes
TR_GITHUB_OAUTH_REDIRECT_URL None yes
TR_GITHUB_OAUTH_LOGIN_AVAILABLE None
TR_GITHUB_ALIAS_CREDENTIALS_JSON "{}" GitHub OAuth Apps permit exactly one callback URL. yes
TR_FEDERATION_PEER_TOKEN "" --- Lazy API-key federation ------------------------------------------- HOME side: the token peers present to /internal/federation/resolv…
TR_FEDERATION_HOME_BASE_URL "" PEER side: the home plane this deployment resolves unknown keys from, and the token it presents.
TR_FEDERATION_HOME_TOKEN ""
TR_FEDERATION_CREDIT_INBOUND_TOKEN "" INBOUND: what this plane requires to be credited.
TR_FEDERATION_CREDIT_PEER_BASE_URL "" OUTBOUND: the plane this deployment may push credits TO, and the token it presents there.
TR_FEDERATION_CREDIT_PEER_TOKEN ""
TR_FEDERATION_DEFERRED_SETTLEMENT_ENABLED False OFF by default.
TR_FEDERATION_DEFERRED_MAX_OUTSTANDING_MICRODOLLARS 25000000 The bound on how much unsettled debt one workspace may run up on this plane.
TR_FEDERATION_DEFERRED_AUTHORIZATION_TTL_SECONDS 7200 How long a deferred authorization may sit unsettled before the reaper reclaims its estimate and releases the key-limit escrow.
TR_FEDERATION_SETTLEMENT_INBOUND_TOKENS "" HOME side: which peers may apply usage to this plane's ledger, as "plane=token,plane=token".
TR_FEDERATION_SETTLEMENT_HOME_TOKEN "" PEER side: the token this plane presents to the home plane's apply-usage endpoint.
TR_FEDERATION_SETTLEMENT_WORKSPACE_DAILY_CAP_MICRODOLLARS 100000000 HOME's aggregate clamp: maximum usage one peer may apply to one workspace per UTC day.

Rate limiting & admission (7)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_RATE_LIMIT_ENABLED True
TR_RATE_LIMIT_WINDOW_SECONDS 60
TR_RATE_LIMIT_IP_PER_WINDOW 240
TR_RATE_LIMIT_KEY_PER_WINDOW 1200
TR_RATE_LIMIT_INTERNAL_PER_WINDOW 6000
TR_SETTLE_PER_KEY_INFLIGHT_LIMIT 16 Settlement has a wider gate because it closes already-issued holds, but one hot key must not consume every Spanner session in an instance.
TR_RATE_LIMIT_CLIENT_IP_MODE "untrusted" Only front doors that overwrite X-TrustedRouter-Client-IP may opt into edge_header.

Billing & payments (Stripe/PayPal/Adyen/Routable/x402/Veriff) (49)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_STRIPE_WEBHOOK_SECRET None yes
TR_STRIPE_SECRET_KEY None yes
TR_STRIPE_CARD_FEE_BASIS_POINTS 290 Standard US Stripe processing schedules.
TR_STRIPE_CARD_FEE_FIXED_CENTS 30
TR_STRIPE_STABLECOIN_FEE_BASIS_POINTS 150
TR_STRIPE_STABLECOIN_FEE_FIXED_CENTS 0
TR_STRIPE_ACH_FEE_BASIS_POINTS 80
TR_STRIPE_ACH_FEE_FIXED_CENTS 0
TR_STRIPE_ACH_FEE_MAX_CENTS 500
TR_PAYPAL_CLIENT_ID None yes
TR_PAYPAL_CLIENT_SECRET None yes
TR_PAYPAL_WEBHOOK_ID None yes
TR_PAYPAL_API_BASE_URL "https://api-m.paypal.com" yes
TR_ROUTABLE_ENABLED False Creator USD cash-outs. yes
TR_ROUTABLE_API_TOKEN None yes
TR_ROUTABLE_WEBHOOK_SECRET None yes
TR_ROUTABLE_COMPANY_ID None yes
TR_ROUTABLE_TEAM_MEMBER_ID None yes
TR_ROUTABLE_WITHDRAW_FROM_ACCOUNT_ID None yes
TR_ROUTABLE_API_BASE_URL "https://api.routable.com" yes
TR_ADYEN_ENABLED False Adyen is staged dark until the test merchant, HMAC webhook, and end-to-end checkout canary are all green.
TR_ADYEN_API_KEY None yes
TR_ADYEN_CLIENT_KEY None yes
TR_ADYEN_HMAC_KEY None yes
TR_ADYEN_REFERENCE_KEY None yes
TR_ADYEN_MERCHANT_ACCOUNT None yes
TR_ADYEN_ENVIRONMENT "test"
TR_ADYEN_LIVE_ENDPOINT_PREFIX None yes
TR_ADYEN_CHECKOUT_API_VERSION 72
TR_ADYEN_WEB_VERSION "6.41.0"
TR_ADYEN_CARD_FEE_BASIS_POINTS 0 Keep processor pricing explicit.
TR_ADYEN_CARD_FEE_FIXED_CENTS 0
TR_X402_ENABLED False
TR_X402_ALLOW_MOCK_PAYMENTS False
TR_X402_NETWORK "base"
TR_X402_NETWORK_ID "eip155:8453"
TR_X402_STRIPE_API_VERSION "2026-03-04.preview"
TR_X402_MAX_FUND_DOLLARS "500"
TR_X402_RATE_LIMIT_WINDOW_SECONDS 60
TR_X402_RATE_LIMIT_KEY_PER_WINDOW 10
TR_X402_RATE_LIMIT_WORKSPACE_PER_WINDOW 30
TR_X402_SETTLE_RATE_LIMIT_PER_WINDOW 30
TR_X402_SETTLE_WORKSPACE_PER_WINDOW 120
TR_VERIFF_ENABLED False yes
TR_VERIFF_API_KEY None yes
TR_VERIFF_SHARED_SECRET_KEY None yes
TR_VERIFF_BASE_URL "https://stationapi.veriff.com" yes
TR_TRUST_STRIPE_ACCOUNT_ID "" Same account pin used by the deployed backfill and reconciliation jobs.
TR_LIGHTNING_FUNDING_TOKEN ""

Money path: settle/outbox/async settle/fastpath (50)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_SPECULATION_SHADOW_WORKSPACES []
TR_SPECULATION_SHADOW_ROUTES []
TR_SPECULATION_SHADOW_IMAGES []
TR_SPECULATION_SHADOW_PRODUCERS []
TR_SPECULATION_SHADOW_PRODUCER ""
TR_SPECULATION_SHADOW_SLOTS {}
TR_SPECULATION_SHADOW_IMAGE_POLICY_VERSION 0
TR_SPECULATION_SHADOW_POLICY_EXPIRES_AT 0
TR_SPECULATION_SHADOW_PLANE "gcp"
TR_SPECULATION_SHADOW_ISSUER ""
TR_SPECULATION_SHADOW_AUDIENCE ""
TR_SPECULATION_SHADOW_KID ""
TR_SPECULATION_SHADOW_PRIVATE_KEY_FILE "" Independently provisioned mounted file.
TR_ASYNC_SETTLE_SHADOW_ATTACHMENT_BUCKET "" Dormant async v1 authorize metadata;
TR_ASYNC_SETTLE_SHADOW_POLICY ""
TR_ASYNC_SETTLE_SHADOW_HEALTH_REGION ""
TR_ASYNC_SETTLE_SHADOW_WORKSPACES ""
TR_ASYNC_SETTLE_PILOT_WORKSPACES ""
TR_ASYNC_SETTLE_ENABLED False
TR_ASYNC_SETTLE_PROTECTION False
TR_ASYNC_SETTLE_TICKET_KID ""
TR_ASYNC_SETTLE_TICKET_ISSUER ""
TR_ASYNC_SETTLE_TICKET_AUDIENCE ""
TR_ASYNC_SETTLE_TICKET_PRIVATE_KEY_FILE "" Independently mounted purpose key.
TR_ASYNC_SETTLE_PILOT_CAP_MICRO 0
TR_ASYNC_SETTLE_TICKET_TTL_SECONDS 300
TR_ASYNC_SETTLE_AUTHORITY_EPOCH 0 Operator-owned local settlement epoch, NOT the billing pause epoch or shadow grant generation.
TR_STAGE_D_HEARTBEAT_ENABLED True Stage D is inert until an attested enclave calls the new endpoint.
TR_STAGE_D_REQUIRE_HEARTBEAT_DECLARATION False Off until every accepted enclave image declares its heartbeats at boot registration.
TR_STAGE_D_ELIGIBILITY_ENABLED False Emergency kill added 2026-09-03: declares no request Stage D eligible.
TR_STAGE_D_PILOT_WORKSPACE_IDS "45819281-0ce9-4811-a0cd-c660ab3a116d"
TR_FASTPATH_PILOT_ENABLED False The fast path's pilot (docs/design/fast-admission-pilot.md): an authorize for a workspace in the set, from a control plane in one of the …
TR_FASTPATH_PILOT_WORKSPACES ""
TR_FASTPATH_PILOT_REGIONS "us-central1"
TR_FASTPATH_FRONT_DOOR_TIMEOUT_SECONDS 1.5 The front door answers an admitted authorize in a few milliseconds;
TR_STAGE_D_POLICY_REFRESH_SECONDS 60
TR_STAGE_D_POLICY_CERT_IDENTITY "https://github.com/Lore-Hex/quill-cloud-prox…
TR_STAGE_D_POLICY_OIDC_ISSUER "https://token.actions.githubusercontent.com"
TR_SETTLE_OUTBOX_FAST_DRAIN_ENABLED False Durable settle outbox (docs/design/durable-settle-outbox.md).
TR_SETTLE_OUTBOX_POLL_INTERVAL_SECONDS 300
TR_SETTLE_OUTBOX_HEALTH_PUBLISH_INTERVAL_SECONDS 2
TR_SETTLE_OUTBOX_CLAIM_BATCH 500
TR_SETTLE_OUTBOX_WORKER_CONCURRENCY 1
TR_SETTLE_OUTBOX_LEASE_SECONDS 300
TR_SETTLE_OUTBOX_PASS_BUDGET_SECONDS 240
TR_SETTLE_OUTBOX_ENABLED False
TR_STAGE_D_PROBE_API_KEY "" Second internal probe credential, used by the enclave rollout gate for the Stage D cohort.
TR_ASYNC_SETTLE_SHADOW_PROBE_API_KEY "" yes
TR_ASYNC_SETTLE_SHADOW_PROBE_REPETITIONS 1
TR_ASYNC_SETTLE_SHADOW_PROBE_CADENCE_MINUTES 4

Trust / attestation (24)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_TRUST_GCP_SOURCE_COMMIT None
TR_TRUST_GCP_IMAGE_REFERENCE None
TR_TRUST_GCP_IMAGE_DIGEST None
TR_TRUST_GCP_RELEASE_URL "" Production control-plane trust aliases resolve the current gateway release from this canonical, independently published record.
TR_TRUST_GCP_RELEASE_FALLBACK_URLS "" Independently hosted copies keep the backup-domain trust pages live when the canonical DNS zone is unavailable.
TR_TRUST_AWS_SOURCE_COMMIT None Both are SETS, not scalars.
TR_TRUST_AWS_IMAGE_REFERENCE None
TR_TRUST_AWS_PCR0 None
TR_TRUST_AWS_ACCEPTED_PCR0S ""
TR_TRUST_AZURE_SOURCE_COMMIT None
TR_TRUST_AZURE_IMAGE_REFERENCE None
TR_TRUST_AZURE_HOSTDATA None
TR_TRUST_AZURE_ACCEPTED_HOSTDATA ""
TR_TRUST_AZURE_ATTESTATION_ISSUERS "" Azure serves from more than one region and each region has its own MAA instance, so the issuer a verifier sees depends on which region an…
TR_TRUST_AWS_RELEASE_URL "" Where each plane's AUTHORITATIVE record lives.
TR_TRUST_AZURE_RELEASE_URL ""
TR_TRUST_QUALIFYING_PROVIDERS "stripe,x402"
TR_TRUST_TIER3_MIN_DAYS 30
TR_TRUST_TIER3_MIN_PAID_MICRODOLLARS 50000000
TR_TRUST_TIER_JOB_CONCURRENCY 1 Workspaces the trust-tier job recomputes at once.
TR_TRUST_TIER_SHADOW_ENABLED True Shadow the per-workspace pass with one bulk snapshot (trust_tier_bulk.py) and log any workspace it acted on that the bulk selection missed.
TR_TRUST_RECONCILE_INTERVAL_SECONDS 900
TR_TRUST_RECONCILE_MAX_AGE_SECONDS 3600
TR_ATTESTATION_EXPECTED_PCR0 None Pin the enclave's PCR0 (EIF measurement).

Observability (Sentry/Axiom/synthetic/remediator) (45)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_CLIENT_EVENTS_ENABLED False Client-observed reliability beacons remain off until the ClickHouse node runbook has been completed.
TR_CLIENT_EVENTS_SUCCESS_SAMPLE_RATE 0.01 Sampling policy returned to SDKs for ordinary successful requests.
TR_CLIENT_EVENTS_PAUSE_SECONDS 0 A positive value pauses SDK delivery without reading or storing a body.
TR_CLIENT_EVENTS_SYNTHETIC_WORKSPACE_IDS [] Workspaces whose beacons are synthetic regardless of the client bit.
TR_CLIENT_EVENTS_MAX_BODY_BYTES 65536 Hard request and abuse-control bounds for the fire-and-forget endpoint.
TR_CLIENT_EVENTS_KEY_PER_MINUTE 60
TR_CLIENT_EVENTS_WORKSPACE_PER_MINUTE 300
TR_CLIENT_EVENTS_WRITE_CONCURRENCY 4 Bounds blocking outbox writes so telemetry cannot consume the money path's pool.
TR_CLIENT_EVENTS_FLUSH_SECONDS 30 Flush cadence returned to SDKs in the accepted policy.
TR_SENTRY_DSN None yes
TR_SENTRY_TRACES_SAMPLE_RATE 0.05
TR_SENTRY_LOCAL_ENABLED False
TR_AXIOM_DATASET "trusted-router-logs" Axiom log shipping.
TR_AXIOM_URL "https://api.axiom.co"
TR_AXIOM_LOG_LEVEL "INFO" Levels at and above this go to Axiom.
TR_SENTRY_FLOODGATE_ENABLED True
TR_SENTRY_FLOODGATE_WINDOW_SECONDS 3600
TR_SENTRY_FLOODGATE_MAX_EVENTS_PER_FINGERPRINT 3
TR_SENTRY_FLOODGATE_MAX_EVENTS_PER_WINDOW 50
TR_SENTRY_FLOODGATE_MAX_FINGERPRINTS 2048
TR_SYNTHETIC_MONITOR_REGION None
TR_SYNTHETIC_MONITOR_API_KEY None yes
TR_SYNTHETIC_MONITOR_MODEL "trustedrouter/monitor"
TR_SYNTHETIC_CONTROL_PLANE_BASE_URL None Exact HTTPS control-plane origin synthetic canaries exercise.
TR_SYNTHETIC_CANONICAL_ATTESTED False The canonical target serves a self-signed cert minted inside the TEE (AWS Nitro standalone deployments): probes skip CA verification and …
TR_SYNTHETIC_SCHEDULER_INTERVAL_SECONDS 0 0 = disabled (the default, so GCP/AWS keep their existing schedulers and nothing double-runs).
TR_SYNTHETIC_SCHEDULER_ROTATION_COUNT 8 Completions per pass.
TR_SYNTHETIC_REGIONAL_PROBES_ENABLED True Per-region alias/Cloud-Run probing is a GCP-topology concept: it derives api-{region}.quillrouter.com aliases and *.run.app direct URLs f…
TR_SYNTHETIC_IMAGE_PROBE_ENABLED True Image generation is sampled by a SEPARATE scheduled job (-m trusted_router.synthetic.image_generation), not by the 1-minute synthetic run.
TR_SYNTHETIC_STATUS_PROBE_TYPES "" Public readers describe the monitor plane, not their own credentials.
TR_SYNTHETIC_CONTROL_PLANE_HEALTH_URL None Explicit control-plane /health URL attached to the canonical target.
TR_SYNTHETIC_GATEWAY_REGION_TARGETS "" Empty = today's behaviour, exactly one canonical target.
TR_SYNTHETIC_MONITOR_TIMEOUT_SECONDS 20.0 Per-probe HTTP timeout for real provider-effective synthetic checks.
TR_SYNTHETIC_RUN_DEADLINE_SECONDS 240.0 Hard wall-clock ceiling for one admitted synthetic run.
TR_SYNTHETIC_REMEDIATOR_DEADLINE_SECONDS 15.0 Remediation is an observe-only monitor read running through an internal HTTP surface during the service split.
TR_SYNTHETIC_MONITOR_MONTHLY_GRANT_DOLLARS 200.0 Monthly self-funding for the monitor workspace, applied lazily on its own gateway-authorize path (synthetic/funding.py).
TR_SYNTHETIC_STATUS_SAMPLE_LIMIT 5000
TR_SYNTHETIC_STATUS_RAW_RETENTION_DAYS 14
TR_SYNTHETIC_STATUS_ROLLUP_RETENTION_MONTHS 24
TR_SYNTHETIC_STATUS_US_URL "https://status-us.trustedrouter.com/status.j…
TR_SYNTHETIC_STATUS_EU_URL "https://status-eu.trustedrouter.com/status.j…
TR_REMEDIATOR_MODE "observe" Fleet peer list ("name=base_url,..."): every deployment watches every deployment's public status page — its own included, which proves th…
TR_REMEDIATOR_INTERVAL_SECONDS 120
TR_REMEDIATOR_IN_PROCESS_ENABLED True Cloud Run request-based CPU may pause background coroutines between requests.
TR_SYNTHETIC_FLEET_PEERS "gcp=https://trustedrouter.com,aws=https://aw…

Notifications & email (SES/Twilio/Telnyx/ops chat) (33)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_OPS_CHAT_WEBHOOK_URLS "" Optional three-cloud Matrix support fanout.
TR_OPS_CHAT_WEBHOOK_SECRET None
TR_OPS_CHAT_WEBHOOK_TIMEOUT_SECONDS 3.0
TR_AWS_ACCESS_KEY_ID None Amazon SES — used for optional wallet email attach/verification. yes
TR_AWS_SECRET_ACCESS_KEY None yes
TR_AWS_REGION "us-east-1" yes
TR_SES_FROM_EMAIL None yes
TR_SES_FROM_NAME "TrustedRouter" yes
TR_SES_CONFIGURATION_SET "trustedrouter-default" Configuration set used on every SendEmail call so SES emits bounce + complaint events to our SNS topic (subscribed at /internal/ses/notif…
TR_SES_ALERT_FROM_EMAIL "alerts@alerts.trustedrouter.com" Operational alerts use a dedicated authenticated subdomain and configuration set. yes
TR_SES_ALERT_FROM_NAME "TrustedRouter Alerts" yes
TR_SES_ALERT_CONFIGURATION_SET "trustedrouter-alerts" yes
TR_SES_NOTICES_FROM_EMAIL "notices@notices.trustedrouter.com" Customer product notices (route retirements) send from their own authenticated subdomain and configuration set, separate from sign-in mai… yes
TR_SES_NOTICES_FROM_NAME "TrustedRouter" yes
TR_SES_NOTICES_CONFIGURATION_SET "trustedrouter-notices" yes
TR_NOTIFY_ENABLED False ---- notifications (email / sms / voice to the account owner) ---------- Delivery credentials.
TR_NOTIFY_SMS_AVAILABLE False Flip to True once A2P 10DLC is approved on the primary SMS carrier;
TR_TELNYX_API_KEY None
TR_TELNYX_FROM_NUMBER None
TR_NOTIFY_VOICE_REPEAT_UNANSWERED True The ORGANIZATION id from Telnyx /v2/whoami — not the number's connection id and not the TeXML application id, both of which answer 404 here.
TR_NOTIFY_SMS_PRIMARY_CARRIER "twilio"
TR_NOTIFY_VOICE_PRIMARY_CARRIER "telnyx"
TR_TELNYX_TEXML_ACCOUNT_ID None
TR_TELNYX_TEXML_APPLICATION_ID None The TeXML application, which is where the outbound voice profile hangs.
TR_TWILIO_ACCOUNT_SID None
TR_TWILIO_AUTH_TOKEN None
TR_TWILIO_API_KEY_SID None
TR_TWILIO_API_KEY_SECRET None
TR_TWILIO_FROM_NUMBER None
TR_NOTIFY_PRICE_MICRODOLLARS 20000 $0.02 is positive on every path, thinnest on a Twilio VOICE fallback (~$0.006 margin on a one-minute minimum).
TR_NOTIFY_PUSH_PRICE_MICRODOLLARS 0 It is also the only channel with no carrier and no regulator in the path, which makes it the one we can offer on day one without 10DLC or…
TR_NOTIFY_MAX_PER_HOUR 30 Per-workspace ceilings.
TR_NOTIFY_MAX_VOICE_PER_HOUR 4
Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_LEGAL_ENTITY_NAME "Lore Hex Corp"
TR_LEGAL_ENTITY_TYPE "Delaware C Corporation"
TR_LEGAL_ENTITY_ADDRESS "1111 Brickell Ave, Floor 10, Miami, FL 33131"
TR_LEGAL_ENTITY_STREET "1111 Brickell Ave, Floor 10" Structured components of the same address.
TR_LEGAL_ENTITY_CITY "Miami"
TR_LEGAL_ENTITY_REGION "FL"
TR_LEGAL_ENTITY_POSTAL_CODE "33131"
TR_LEGAL_ENTITY_COUNTRY "US"
TR_LEGAL_ENTITY_PHONE "+1-305-239-7350"
TR_LEGAL_ENTITY_EIN "41-5339728"
TR_LEGAL_ENTITY_DUNS "144992055"
TR_LEGAL_ENTITY_DATE_ESTABLISHED None
TR_LEGAL_SIGNATORY_NAME "Joseph Perla"
TR_LEGAL_SIGNATORY_TITLE "CEO"

Other (21)

Env var Default Purpose (from code comment, first sentence) Keys-file fallback
TR_ANALYTICS_OUTBOX_ENABLED False Stage 1 live analytics outbox.
TR_NEW_SIGNUPS_ENABLED True Global, creation-only operational brake.
TR_EMAIL_SIGNUP_ENABLED False Plain-email POST /v1/signup (no OAuth) is a credit-farming vector: an open endpoint that mints a management key + trial credit for ANY …
TR_ENABLE_SENTRY_TEST_ROUTE False
TR_GATEWAY_AUTHORIZE_MAX_IN_FLIGHT_PER_KEY 4 A contended Spanner authorize can occupy a worker for the full RPC budget.
TR_OBSERVER_INTERNAL_TOKEN None Dedicated to the externally reachable observer/status processes.
TR_CHECKOUT_CARD_FEE_MINIMUM_CENTS 80 Card-style rails have a purchase-fee floor.
TR_AUTH_SESSION_TTL_SECONDS 2592000
TR_OAUTH_AUTHORIZATION_CODE_TTL_SECONDS 600
TR_PARTNER_INQUIRY_EMAIL None Destination for TrustedOS partner-inquiry form submissions (/trustedos).
TR_ACTIVATION_REMINDER_INTERVAL_SECONDS 0 Durable post-signup activation reminders.
TR_MULTI_REGION_ENABLED True
TR_PHONE_VERIFICATION_REQUIRES_FUNDING None
TR_CUSTOM_MODELS_REQUIRE_VERIFICATION None
TR_IDENTITY_SESSION_STALE_AFTER_DAYS 7
TR_HEARTBEAT_GRACE_SECONDS 300
TR_REQUEST_RECORD_WRITE_MODE "legacy" Expand/contract switch for per-request Spanner records.
TR_BROADCAST_INLINE_DRAIN_ENABLED False Local/test drains broadcast jobs opportunistically after settlement so tests and demos are deterministic.
TR_MARKETING_REGIONS "us-central1,europe-west4,us-east4,us-west1,a…
TR_EXTERNAL_LIVE_REGIONS "aws-eu-west-1,aws-eu-west-3,azure-australiae… Non-GCP deployments that have PASSED their own end-to-end smoke (verify_deployment.sh --expect-monitor).
TR_AUTO_MODEL_ORDER "" IDs follow OpenRouter naming exactly to line up with the ingest snapshot — moonshotai/... not kimi/..., mistralai/... not `mistral/…

8. Troubleshooting (real issues hit during verification)

# Symptom Cause Fix
T1 failed to connect to the docker API at unix:///Users/…/.docker/run/docker.sock … no such file or directory Docker CLI installed but the daemon was not running Start Docker Desktop, OrbStack or colima. You only need it for the postgres/spanner conformance backends (commands in AGENTS.md, ports 5433/5434, TR_CONFORMANCE_*_DSN). Not verified on this Mac.
T2 POST /v1/chat/completions → 402 Insufficient credits on a fresh local workspace Header-auth users get no trial credit, and the memory store starts at $0 Credit yourself with the unsigned local Stripe webhook in §4c
T3 400 model_not_supported: cerebras/llama3.1-8b The catalog moved on, and old examples (README smoke script) still use this model Use trustedrouter/auto or pick an ID from /v1/models
T4 smoke_e2e.py: expected 54 OpenRouter route classifications, got 95 The script's hard-coded expectations are stale Use the §4 curl flow. Fixing the script is a good first PR.
T5 Provider keys "not found" even though TR_LOCAL_KEYS_FILE points at your file / you copied .env.example .env.example hard-codes Joseph's path. The Settings fallback always reads the default path anyway (§6 gotcha). Put the file at ~/.quill_cloud_keys.private, or export the provider keys in the shell
T6 Changing a TR_* var "does nothing" Typo (extra="ignore"), or the var is not a Settings field (e.g. TR_BIGTABLE_* in .env.example), or the shell env is overriding .env uv run python -c "from trusted_router.config import get_settings as g; print(g().<field>)"
T7 App refuses to start: TR_SERVICE_SURFACE=combined is restricted to local/test… You set TR_ENVIRONMENT to something other than local/test Keep local for dev. Deployed surfaces are configured only in rollout.sh.
T8 /healthz 404 The route doesn't exist Use /health or /v1/health
T9 Local bursts get 429 TR_RATE_LIMIT_ENABLED=true by default (240 req/min per IP) TR_RATE_LIMIT_ENABLED=false for load or browser testing
T10 Your keys file is ignored in tests By design: _running_under_pytest() skips it TR_ALLOW_LOCAL_KEY_FILE_IN_TESTS=1 (rarely what you want)
T11 CI frontend job fails on git diff --exit-code You edited frontend/src/*.ts without committing the rebuilt JS npx tsc, then commit src/trusted_router/static/*.js
T12 Lots of on_event is deprecated warnings in pytest FastAPI lifespan deprecation Harmless
T13 A test fails on your branch but passes on main main moves fast (an hourly price-refresh bot commits to it) Rebase first (AGENTS.md)

9. Safety reminders for local work

  • Local dev needs zero cloud credentials. If something asks for gcloud auth, stop and check TR_STORAGE_BACKEND.
  • Never point TR_SPANNER_* / TR_POSTGRES_DSN at production. AGENTS.md: unindexed tr_entities.body searches caused the 2026-09-06 CPU incident. Use python -m scripts.inspect_payment_owner for Stripe attribution and ClickHouse for analytics.
  • Pushing to main auto-deploys the control plane when src/**, scripts/deploy/**, frontend/src/**, Dockerfile, pyproject.toml or uv.lock change.
  • Money code (reserve/settle/refund) changes need a differential or conformance test, never review-by-eye.

10. Verification log (raw timings)

Step Command Outcome Wall time
sync uv sync ✅ 238 resolved 9.8 s (warm cache)
lint uv run ruff check . ✅ 0.55 s
types uv run mypy ✅ 533 files 9.6 s warm / 8.3 s cold cache
conformance subset pytest tests/conformance/{store_semantics,key_management} -k memory ✅ 90 passed / 14 skipped 10.2 s
unit subset pytest tests/test_billing.py tests/test_inference_errors.py ✅ 82 passed 10.4 s
collect pytest --collect-only -q ✅ 38,221 selected 31.6 s
catalog check scripts/update_model_changes.py --check ✅ exit 0 —
server uvicorn … --port 18080 → health, models, keys, webhook credit, chat, stream, activity ✅ about 1 s startup
smoke script scripts/smoke_e2e.py ⚠️ stale assertion (54 vs 95) —
npm npm ci, lint, lint:css, typecheck, npx tsc (no diff) ✅ < 1 s each
playwright npx playwright install chromium + homepage.spec.js ✅ 9 passed 12 s + 5.7 s
docker conformance postgres / pgadapter ⚠️ not run (daemon down) —