
TrustedRouter.com repo for secure LLM proxying
Stop worrying about who can see your prompts. Tell your coding agent to move your project over, pick how private you want to be, pick a model, drop in a key — done. Same API, 30+ models, one key. The gateway runs in hardware enclaves and you can cryptographically verify it never logs you.
Paste this into Codex, Claude Code, or Cursor — it does the migration for you:
Migrate this project to TrustedRouter, a privacy-first LLM router
(https://trustedrouter.com). Repoint my LLM client to base_url
"https://api.trustedrouter.com/v1" (or "https://api.trustedrouter.com" for the
Anthropic SDK), read the key from the TRUSTEDROUTER_API_KEY env var, and keep
all my existing calls working.
For a hard provider-side confidential-compute and end-to-end-encryption
requirement, add {"provider": {"min_privacy": "confidential"}}. TrustedRouter
fails closed when the selected model or provider cannot satisfy both controls.
Then tell me to sign up at trustedrouter.com, add a card, and paste my sk-tr
key into TRUSTEDROUTER_API_KEY.
Then:
{"provider": {"min_privacy": "zdr"}} for a hard zero-retention floor, or
{"provider": {"min_privacy": "confidential"}} for the stronger hard
confidential-compute + E2EE floor. The convenient trustedrouter/zdr and
trustedrouter/e2e (trustedrouter/confidential) aliases select those
pools. Use trustedrouter/eu with
https://api-europe-west4.quillrouter.com/v1 for EU-focused routing.trustedrouter/auto for automatic
fallback when provider breadth matters more than the strictest privacy
filter.# Codex
export OPENAI_BASE_URL="https://api.trustedrouter.com/v1"
export OPENAI_API_KEY="sk-tr-v1-..."
# Claude Code
export ANTHROPIC_BASE_URL="https://api.trustedrouter.com"
export ANTHROPIC_API_KEY="sk-tr-v1-..."
# Any OpenAI SDK
client = OpenAI(base_url="https://api.trustedrouter.com/v1", api_key="sk-tr-v1-...")
TrustedRouter's gateway runs inside GCP Confidential Space. The platform signs a measurement of the running binary; you compare that hash to this repo. If they match, you know — not assume — that the code handling your prompts is the code you can read here, and that it never writes your prompts to disk.
Verify it yourself in 60 seconds, no account:
NONCE=$(openssl rand -hex 16)
curl -s "https://api.trustedrouter.com/attestation?nonce=$NONCE" | jq .
# eat_nonce your nonce (replay-protected)
# image_digest SHA-256 of the running container
# pcrs boot-time platform measurements
# Compare image_digest to the published artifact at
# https://trustedrouter.com/security — match = the running code is this repo.
This repo implements the control-plane contract: route coverage, auth/key
management, billing ledger semantics, usage metadata, no prompt/output storage,
Sentry scrubbers, and provider abstractions. The attested gateway
implementation lives in quill-cloud-proxy.
Trust boundary: api.trustedrouter.com is the attested prompt path and must
terminate TLS inside Confidential Space. trustedrouter.com is the control
plane and must never serve a production inference fallback.
api.quillrouter.com remains a permanent working alias (same attested
gateway and cert), so existing integrations keep working with no migration.
uv sync
uv run pytest
uv run uvicorn trusted_router.main:app --reload
End-to-end smoke against a running instance:
TR_SMOKE_BASE_URL=http://127.0.0.1:18080/v1 uv run python scripts/smoke_e2e.py
For production, set TR_SMOKE_BASE_URL=https://api.trustedrouter.com/v1 and
TR_SMOKE_INTERNAL_TOKEN if the internal gateway routes are token-protected.
Set local operator/provider keys in:
/Users/jperla/claude/.quill_cloud_keys.private
That file is never committed. It is expected to be dotenv-style:
ANTHROPIC_API_KEY=...
OPENAI_API_KEY=...
GEMINI_API_KEY=...
CEREBRAS_API_KEY=...
DEEPSEEK_API_KEY=...
MISTRAL_API_KEY=...
STRIPE_SECRET_KEY=...
STRIPE_WEBHOOK_SECRET=...
SENTRY_DSN=...
The deploy script also accepts local aliases already used in some operator
files: CLAUDE_API_KEY for Anthropic, CHATGPT_API_KEY for OpenAI, and
STRIPE_KEY for STRIPE_SECRET_KEY.
Vertex is different from the other provider platforms: production GCP deploys use the Cloud Run or Confidential Space service account and short-lived Google access tokens from metadata/ADC. Do not put a long-lived Vertex key in this file for the first-party prepaid Vertex route; grant the runtime service account Vertex permissions instead.
Business Source License 1.1. The source is public so anyone can read, build, and verify the exact code behind TrustedRouter's privacy and attestation claims (https://trust.trustedrouter.com) — that is what it is here for. Non-production use (security review, audit, local evaluation) is free. Production use requires a commercial license from Lore Hex Corp: [email protected]. Each version converts to the Apache License 2.0 four years after publication. Code published before July 3, 2026 remains Apache-2.0.
env://... references remain supported for
operator-managed keys.byok_cache_key for encrypted
BYOK envelopes. Attested gateways use it for short TTL, memory-only decrypted
key caching; BYOK rotation changes the key and delete stops returning the
envelope./chat/completions,
/messages, /responses, or /embeddings; those belong on the attested API
plane.Workspace owners can configure Broadcast destinations at
/v1/broadcast/destinations or in the console under Broadcast. Supported
destinations are PostHog and OTLP JSON webhooks. Broadcast is metadata-only by
default: model, provider, token counts, latency, cost, route type, region, and
custom trace metadata. Prompt/output content is exported only when a destination
explicitly enables include_content; those content-enabled encrypted
destinations are returned only to the attested gateway, not normal management
responses. Metadata-only deliveries are written to a persistent Broadcast
outbox first and drained asynchronously by /internal/broadcast/drain, so a
PostHog/webhook outage does not block inference or lose already-settled
metadata on process restart.
TrustedRouter has a separate synthetic monitoring plane for public uptime. Synthetic workers run outside the enclave, send tiny real requests into the public attested API, and store only metadata. The monitor model aliases are:
trustedrouter/free: OpenRouter-style free pool. Useful for users, not an
SLA signal.trustedrouter/cheap: cheapest paid pool with provider diversity.trustedrouter/eu: EU-focused provider pool. It prefers European,
EU-regionable, and privacy-forward providers, especially when paired with
https://api-europe-west4.quillrouter.com/v1. This is a routing policy, not
a blanket data-residency guarantee.trustedrouter/monitor: internal uptime pool for PONG and fallback checks.
It is visible in the catalog for transparency, but authorization requires
the configured TR_SYNTHETIC_MONITOR_API_KEY; normal API keys receive 403.Workers should run from us-central1 and europe-west4, using a dedicated
trustedrouter-synthetic-monitoring workspace/key with hard spend caps and
auto-refill. Raw samples are append-only Bigtable rows; public status pages
read compact rollups exposed at /status, /status.json, and
/status/history?window=5m|24h|daily. Synthetic generations use the
TrustedRouter Synthetic app label and are excluded from customer/app
analytics.
Provider measurement uses two independent probe classes:
us-central1. It measures output
tokens per second after the first token. It runs in a separate Cloud Run Job,
so slow streams cannot delay uptime probes. Long-probe failures never count
against provider uptime or API-drift alerts.The current two-minute schedule gives each sustained route about 25 samples per week and 108 per 30 days. CI calculates a full-cap spend estimate from the live catalog and fails if it exceeds the reviewed monthly ceiling.
Status separates two service SLO classes instead of blending them with upstream-provider behavior:
router_core: attested API reachable, key authorization works, route
candidates/fallback are available, and settle/refund is durable.control_plane: dashboard, billing UI, keys, credits, docs, trust, and
status surfaces.Deploy watchdogs and internal burn-rate alerts default to router_core.
Provider-only failures are measured per provider on /status and
/leaderboard; they do not consume the router-core error budget when fallback
remains available.
trustedrouter/auto is a real chat model alias in local/test
control-plane inference and rolls to the next configured provider on upstream
provider failures. trustedrouter/eu prefers the EU-focused provider pool,
trustedrouter/zdr forces a zero-retention provider floor with Anthropic
first, and trustedrouter/e2e forces confidential + E2EE routes with
Tinfoil first. Chat requests also honor OpenRouter-style models and
provider routing filters (order, only, ignore, allow_fallbacks,
min_privacy, data_collection, and sort) so clients can request explicit
fallback chains or provider preferences. is a
hard provider-side confidential-compute + E2EE requirement and fails closed;
the request-value aliases and select the same tier
rather than falling back to a weaker route.The goal is to support OpenRouter-class scale:
The current production deployment does not meet that target yet. It runs the control plane in four GCP regions behind a global LB with per-region Serverless NEGs, with three live attested API regions until additional attested regional pools are deployed. Capacity scales horizontally as more attested pools come online; correctness, trust, billing, and SDK compatibility are in steady-state.
Request volume depends heavily on average generation size. At 1 trillion tokens/day:
| Average tokens/request | Requests/day | Average request rate |
|---|---|---|
| 1,000 | 1.0B | 11.6k rps |
| 2,500 | 400M |
The architecture can be evolved to this scale, but only if the hot path avoids per-request global bottlenecks. That means regional stateless gateway fleets, regional provider pools, sharded quota leases, append-only metadata writes, and asynchronous aggregation.
Measured from this development machine to the centralized GCP us-central1
attested API on May 2, 2026:
The centralized network overhead is much higher than OpenRouter's reported edge overhead, but model latency usually dominates interactive requests. The first production scaling step should be multi-region rather than building a custom global edge immediately.
The production path is designed to scale by keeping the prompt gateway stateless:
api.trustedrouter.com instances can be replicated behind TCP passthrough.
They authorize, reserve, and settle through the control plane, but prompt
bytes never leave the attested path.At OpenRouter-scale traffic, the next bottleneck is not the enclave binary; it is the synchronous billing/authorization path. The architecture needs sharded reservations, regional Bigtable clusters, Cloud Armor edge limits, and multiple gateway replicas before public traffic is allowed to ramp.
Multi-region is feasible while preserving the trust boundary, but it has to be done carefully:
us-central1,
us-east4, and europe-west4, then Asia once the first three regions are
boring.api-us-central1.quillrouter.com,
api-us-east4.quillrouter.com, and api-europe-west4.quillrouter.com for
deterministic attestation, smoke tests, and SDK failover.api.trustedrouter.com behind latency/geo DNS or TCP passthrough that does
not terminate TLS. Cloudflare orange-cloud proxying remains incompatible
with the prompt-path trust claim.The key design rule: a regional outage can fail closed or route to another attested region, but it must never silently degrade to a non-attested prompt handler.
The target is an internal SLO, not a contractual SLA. 99.99% allows about 52 minutes 36 seconds of downtime per year. Public status labels this number as a target until at least 30-60 days of measured 99.99% router-core uptime exists.
Router-core availability means:
The code paths that support this roadmap today are:
/status.json exports slo_classes.router_core,
slo_classes.control_plane, and burn-rate alerts for 5m, 1h, 6h, and 24h
windows.router_core by default, so provider-only outages
do not automatically roll back a control-plane deploy.Before describing four nines as measured availability rather than a target, require three warm GCP attested regions, tested paging, router-core chaos tests, staged regional deploys with rollback gates, and at least 30 days of measured router-core uptime at or above 99.99%.
The attested API plane can reserve and settle usage without sending prompt or output content to the control plane:
POST /v1/internal/gateway/authorize: validates the API key hash, reserves
credits/key limits, and returns provider/BYOK routing metadata, route
candidates derived from model, models, and provider request filters,
and configured regional endpoints.POST /v1/internal/gateway/settle: settles successful usage and appends
metadata-only activity rows.POST /v1/internal/gateway/refund: releases reservations after provider
failures or client disconnects.Set TR_INTERNAL_GATEWAY_TOKEN outside local development.
Production uses:
TR_STORAGE_BACKEND=spanner-bigtable
TR_SPANNER_INSTANCE_ID=trusted-router
TR_SPANNER_DATABASE_ID=trusted-router
TR_BIGTABLE_INSTANCE_ID=trusted-router-logs
TR_BIGTABLE_GENERATION_TABLE=trustedrouter-generations
scripts/deploy-gcp.sh enables the APIs, creates the Spanner table
tr_entities, creates the Bigtable generation table, deploys Cloud Run, and
wires the current GCP trust metadata into the trust page.
POST /v1/billing/checkout creates a Stripe Checkout session when
TR_STRIPE_SECRET_KEY is configured and otherwise returns a deterministic local
mock response. Stripe webhooks credit workspaces idempotently using the
workspace ID in Checkout metadata. Card payments settle immediately. ACH
payments use {"payment_method":"ach"} and are credited only after Stripe sends
checkout.session.async_payment_succeeded; Checkout completion while the debit
is processing never grants credits. POST /v1/billing/portal follows the same
Stripe-or-mock pattern for billing management.
For stablecoin checkout, send {"payment_method":"stablecoin"}. When
TR_STABLECOIN_CHECKOUT_ENABLED=true, the Checkout session is created with
Stripe's crypto payment method and still credits the workspace from the signed
checkout.session.completed webhook.
ACH uses Stripe Checkout's us_bank_account payment method. The default
processing schedule is 0.8% capped at $5 and can be overridden with
TR_STRIPE_ACH_FEE_BASIS_POINTS, TR_STRIPE_ACH_FEE_FIXED_CENTS, and
TR_STRIPE_ACH_FEE_MAX_CENTS. Saved-card auto refill remains card-only.
| trust model |
|---|
| OpenRouter, hosted providers | "We don't log." A policy you can't check. |
| Portkey, Cloudflare AI Gateway | Log everything for observability. |
| LiteLLM | Self-host, but the running proxy is unverified. |
| TrustedRouter | Open source + hardware attestation. Verify the code path; it logs nothing. |
Honest scope: attestation proves the running binary is the published binary on hardware you can challenge with a nonce. It does not defeat a nation-state with physical host access, and it does not prove the open-source binary is bug-free. The trust anchor is Google Confidential Computing's hardware-backed attestation chain. Upstream providers handle prompts per their own policies — each provider's posture is published on the model pages.
min_privacy="confidential"e2ee2eetrust.trustedrouter.com.| 4.6k rps |
| 10,000 | 100M | 1.2k rps |
| Probe | p50 | p95 | Notes |
|---|
Unauthenticated /v1/chat/completions rejection | 174 ms | 184 ms | Includes DNS, TCP, public TLS, enclave request handling. |
| TCP connect | 55 ms | 59 ms | Network path to us-central1 from this machine. |
| TLS handshake complete | 112 ms | 124 ms | Public ACME cert terminates inside the enclave. |
/attestation | 1.06 s | 1.12 s | Includes GCP attestation token generation, so not representative of normal routing overhead. |