Modes
| Mode | Status | How the agent configures it |
|---|---|---|
| HTTPS Prefix | Implemented | BASE_URL=https://t8engine.com/https://api.anthropic.com |
| MCP Proxy | Implemented | BASE_URL=https://t8engine.com/mcp (JWT auth) |
| HTTP Proxy | Implemented | http_proxy=http://t8engine:1800 |
| HTTPS Proxy (MITM) | Implemented | https_proxy=http://t8engine.com:1800 + CA from /ca.crt |
| Hostname | Planned | GH_HOST=gh.t8engine.com |
HTTPS Prefix Mode
The agent prepends the T8 base URL to the upstream API base URL. No SDK changes or custom code required — only environment variables.
Agent configuration
# Before (direct)
ANTHROPIC_API_KEY=sk-ant-real-key
# After (via T8)
ANTHROPIC_API_KEY=<t8-agent-key>
ANTHROPIC_BASE_URL=https://t8engine.com/https://api.anthropic.com
What T8 receives
POST /https://api.anthropic.com/v1/messages HTTP/1.1
Host: t8engine.com
Authorization: Bearer <t8-agent-key>
Content-Type: application/json
What T8 does
- Extracts the target URL from the path (
https://api.anthropic.com/v1/messages). - Finds a matching config entry (prefix + optional
incoming_auth) and resolves credentials — see Credential Resolution. - Strips the T8 agent key and any
incoming_authheaders — they are internal and never forwarded. - Strips hop-by-hop headers and request-scoped framing headers (
host,content-length,content-encoding). - Forwards the request to the upstream with the substituted credentials.
- Streams the response back, preserving status code, headers, and body encoding.
Both http:// and https:// prefixes are accepted.
HTTP Proxy Mode
The agent sets http_proxy=http://t8engine:1800. Any HTTP library that honours the standard http_proxy environment variable will automatically route plain-HTTP requests through T8 — no SDK changes or custom code required.
Agent configuration
export http_proxy=http://t8engine:1800
What the agent sends
GET http://api.example.com/v1/endpoint HTTP/1.1
Host: api.example.com
Authorization: Bearer <t8-agent-key>
What T8 does
- Receives the request with an absolute URI in the request line.
- Extracts the target URL directly from the request URI.
- Applies the same credential resolution as HTTPS Prefix mode (config → control plane → pass-through).
- Forwards the request to the upstream and streams the response back.
This mode covers plain-HTTP upstream APIs. For HTTPS upstreams, use HTTPS Proxy (MITM) Mode, which handles CONNECT tunnelling.
Credential Resolution
T8 resolves credentials through three branches, tried in order. The first match wins.
1. Config file (static)
A TOML file mapping URL prefixes to upstream headers. Loaded once at startup.
[[routes]]
prefix = "https://api.anthropic.com"
[routes.headers]
authorization = "Bearer sk-ant-..."
[[routes]]
prefix = "https://api.openai.com"
[routes.headers]
authorization = "Bearer sk-proj-..."
The first entry whose prefix matches the start of the target URL and whose incoming_auth headers (if any) all match the request is selected. See Config File Format for details.
2. Control plane (dynamic)
When CONTROL_PLANE_URL is set and the request carries a Bearer t8ak_… token (a T8 Agent Key issued by the control plane), T8 exchanges it for upstream credentials:
POST /t8/credentials
Authorization: Bearer t8ak_<agent-key>
{"target_host": "api.anthropic.com"}
→ {"headers": {"authorization": "Bearer sk-ant-real-key"}}
This enables per-agent credential management via the Threshold admin UI without touching any config files.
3. Pass-through
If no config entry matches and no T8 Agent Key is present, the request is forwarded as-is. Non-t8ak_ authorization headers are forwarded unchanged.
HTTPS Proxy (MITM) Mode
T8 acts as an HTTP CONNECT proxy. The agent sets https_proxy, and T8 intercepts each HTTPS tunnel by terminating TLS with a dynamically-generated host certificate signed by the T8 CA.
What T8 does
- Client sends
CONNECT api.anthropic.com:443 HTTP/1.1to T8. - T8 responds
200 Connection Established. - T8 wraps the socket in TLS using a per-hostname cert signed by the T8 CA.
- Decrypted HTTP requests go through the same credential resolution as HTTPS Prefix Mode.
- T8 forwards to the upstream and streams the response back.
Quick install (one-liner)
T8 serves a self-installing script that downloads the CA cert and adds it to the system trust store:
curl -fsSL http://t8engine:1800/ca.sh | sh
The script auto-detects Debian/Ubuntu, Alpine, RHEL/Fedora, Arch, and macOS. It also prints the environment variables needed for Node.js, Python, and curl.
Manual setup
# 1. Download the CA cert
curl -fsSL http://t8engine:1800/ca.crt -o t8-engine-ca.crt
# 2. Install it (pick your platform below)
# 3. Point your agent at T8
export https_proxy=http://t8engine:1800
Debian / Ubuntu / Devcontainers
sudo cp t8-engine-ca.crt /usr/local/share/ca-certificates/t8-engine-ca.crt
sudo update-ca-certificates
Alpine
sudo cp t8-engine-ca.crt /usr/local/share/ca-certificates/t8-engine-ca.crt
sudo update-ca-certificates
RHEL / Fedora / CentOS
sudo cp t8-engine-ca.crt /etc/pki/ca-trust/source/anchors/t8-engine-ca.pem
sudo update-ca-trust extract
macOS
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain t8-engine-ca.crt
Docker (in a Dockerfile)
# Alpine or Debian-based image
COPY t8-engine-ca.crt /usr/local/share/ca-certificates/t8-engine-ca.crt
RUN update-ca-certificates
Language-specific trust
Some runtimes don't use the system trust store by default:
# Node.js
export NODE_EXTRA_CA_CERTS=/path/to/t8-engine-ca.crt
# Python (requests / httpx)
export REQUESTS_CA_BUNDLE=/path/to/t8-engine-ca.crt
# or:
export SSL_CERT_FILE=/path/to/t8-engine-ca.crt
# curl (if system store was not updated)
curl --cacert /path/to/t8-engine-ca.crt ...
CA persistence
By default T8 generates a random CA at startup. Clients must re-install the CA cert after each restart. Three options to make it stable:
Option 1 — Seed (simplest). Set a hex seed and T8 derives a deterministic key pair. No files to manage.
# Generate a seed once:
openssl rand -hex 32
# → e.g. a1b2c3d4...
# Pass it to T8:
T8_CA_SEED=a1b2c3d4... docker compose up -d t8engine
Option 2 — Explicit PEM. Full control over the CA cert and key.
# Generate a persistent CA with openssl (run once):
openssl ecparam -name prime256v1 -genkey -noout | \
openssl pkcs8 -topk8 -nocrypt -out ca.key
openssl req -new -x509 -key ca.key -out ca.crt -days 3650 -subj "/CN=T8 Engine CA"
# Pass it to T8:
T8_CA_CERT=$(cat ca.crt) T8_CA_KEY=$(cat ca.key) docker compose up -d t8engine
Option 3 — None. T8 generates a fresh CA every time. Fine for local development; fetch the cert from GET /ca.crt or run the install script after each restart.
MCP Proxy Mode
Routes under /mcp are reverse-proxied to the configured upstream MCP server. Requests must carry a valid EdDSA JWT issued by the control plane.
Agent → GET /mcp/... → T8 → MCP_UPSTREAM + MCP_UPSTREAM_PATH
JWT validation fetches the JWKS from JWKS_URL on first request and caches it.
Protocol Support
| Protocol | HTTPS Prefix | HTTP Proxy | HTTPS Proxy (MITM) | MCP Proxy |
|---|---|---|---|---|
| HTTP/1.1 request/response | ✅ | ✅ | ✅ | ✅ |
| Server-Sent Events / chunked stream | ✅ | ✅ | ✅ | ✅ |
WebSocket (Upgrade: websocket) | ✅ | ✅ | ✅ | ❌ (rejected) |
| HTTP/2 | ❌ | ❌ | ❌ | ❌ |
Server-Sent Events
Streaming responses (text/event-stream, NDJSON, chunked transfer-encoding) flow through T8 chunk-by-chunk. Each chunk is forwarded to the client as soon as it arrives from the upstream — T8 never buffers the response body.
WebSocket
T8 forwards HTTP upgrade requests through the same credential-resolution flow as regular requests (config entry → control plane → pass-through). After the upstream returns 101 Switching Protocols, T8 pipes the client and upstream sockets bidirectionally and stops inspecting traffic on that connection.
- HTTPS Prefix: clients connect to
wss://t8engine.com/https://target.example.com/ws. The path-prefix is stripped and the request is forwarded. - HTTP Proxy / HTTPS Proxy (MITM): clients use the proxy as normal. T8 sees an
Upgrade: websocketrequest after CONNECT + TLS termination, applies credential resolution, and forwards. - Reserved paths (
/healthz,/ca.crt,/ca.sh,/mcp/*) reject upgrades with 404. - MCP: the upstream MCP server uses streamable-HTTP, not WebSocket, so upgrades on
/mcp/*are intentionally rejected.
HTTP/2
T8 speaks HTTP/1.1 only — both inbound and outbound. Clients that negotiate HTTP/2 will fall back to HTTP/1.1:
- Inbound: Fastify is configured without
http2: true, and MITM TLS sockets do not advertise theh2ALPN protocol. - Outbound: the undici client used to call upstreams is HTTP/1.1.
In practice this is only visible to clients that try to negotiate HTTP/2 with T8 directly. Clients using T8 as an HTTPS proxy (CONNECT) negotiate HTTP/2 with the upstream end-to-end before TLS interception kicks in — which means HTTP/2 over MITM is also unsupported (the MITM TLS terminator forces http/1.1 ALPN).
Configuration (environment variables)
All configuration is via environment variables.
| Variable | Default | Description |
|---|---|---|
PORT | 1800 | Port T8 listens on |
MCP_UPSTREAM | https://threshold.inversed.ai | Upstream MCP server base URL |
MCP_UPSTREAM_PATH | /mcp/test/ | Path prefix rewritten on MCP proxy requests |
JWKS_URL | {MCP_UPSTREAM}/.well-known/jwks.json | JWKS endpoint for MCP JWT verification |
JWT_ISSUER | {MCP_UPSTREAM} | Expected iss claim in MCP JWTs |
CONTROL_PLANE_URL | (unset) | Threshold backend URL for dynamic credential lookup. Omit to disable. |
T8_CONFIG | (unset) | TOML credential config as an inline string. Takes precedence over T8_CONFIG_FILE. |
T8_CONFIG_FILE | (unset) | Path to TOML credential config file. Used only if T8_CONFIG is not set. |
RULE_RUNNER_URL | (unset) | URL of the rule-runner service. When unset, t8 skips rule evaluation (default-allow). |
T8_CA_SEED | (unset) | Hex seed (≥32 chars) to derive a deterministic CA key. Simplest persistence option. |
T8_CA_CERT | (unset) | PEM CA certificate for HTTPS MITM. Overrides T8_CA_SEED if both set. |
T8_CA_KEY | (unset) | PEM EC private key for the CA. Required when T8_CA_CERT is set. |
Config File Format
The TOML config has two top-level sections:
[[routes]]— credential injection + URL rewriting per upstream.[[rules]]— JS/TS permission rules evaluated on every proxied request (see the Rule Scripts reference).
All string values support ${VAR_NAME} interpolation. An unset variable is a startup error; empty string is allowed.
Route entries
Each entry matches requests whose target URL starts with prefix. First match wins. Headers from the matched entry are merged into the outgoing request, overriding anything the client sent under the same names — typically used to swap an agent-facing token for the real upstream credential.
# Minimal entry: inject an Authorization header on every Anthropic request.
[[routes]]
prefix = "https://api.anthropic.com"
[routes.headers]
authorization = "Bearer ${ANTHROPIC_API_KEY}"
Custom header (e.g. x-api-key) instead of Authorization:
[[routes]]
prefix = "https://api.openai.com"
[routes.headers]
"x-api-key" = "${OPENAI_API_KEY}"
Rewrite mode — agent calls https://example/..., T8 forwards to https://actual-api.example.com/.... Useful for hiding the real upstream host behind a stable alias the agent can hard-code:
[[routes]]
prefix = "https://example"
rewrite_prefix = "https://actual-api.example.com"
[routes.headers]
authorization = "Bearer ${EXAMPLE_API_KEY}"
incoming_auth — per-route proxy authentication
[routes.incoming_auth] gates a route behind headers the caller must present. It is part of entry matching: an entry only matches if all incoming_auth headers equal the values in the request. Headers listed in incoming_auth are always stripped before the request is forwarded — they never reach the upstream.
[[routes]]
prefix = "https://api.anthropic.com"
[routes.incoming_auth]
authorization = "Bearer alice-proxy-token" # caller must present this
[routes.headers]
authorization = "Bearer sk-ant-alice" # forwarded upstream instead
Multiple entries for the same host with different incoming_auth values act as per-account routes — the caller's token selects which upstream credential is used:
[[routes]]
prefix = "https://api.openai.com"
[routes.incoming_auth]
authorization = "Bearer alice-proxy-token"
[routes.headers]
authorization = "Bearer sk-openai-alice"
[[routes]]
prefix = "https://api.openai.com"
[routes.incoming_auth]
authorization = "Bearer bob-proxy-token"
[routes.headers]
authorization = "Bearer sk-openai-bob"
A request that matches no entry's incoming_auth falls through to the control plane or pass-through branches — there is no explicit rejection.
Agent key headers
By default T8 scans Authorization, x-api-key, x-goog-api-key, and api-key for a t8ak_… token or a backend-issued JWT. Add to this list only when a target API uses a non-standard header for credentials:
agent_key_headers = ["x-custom-auth"]
Rules
Permission rules live alongside routes as [[rules]] blocks. They run for every proxied request, in declaration order — first deny wins. Full contract: see the Rule Scripts reference.
[[rules]]
id = "block-evil"
script = """
function rule(ctx) {
if (ctx.kind !== 'http') return { action: 'allow' };
if (ctx.host.endsWith('evil.com')) {
return { action: 'deny', reason: 'host blocked' };
}
return { action: 'allow' };
}
"""
The rule-runner URL is configured via the optional RULE_RUNNER_URL env var. When unset, t8 skips rule evaluation entirely (default-allow). The t8 plugin compose file wires this to a bundled rule-runner service automatically.
Running
Via the t8 plugin (recommended)
The t8 marketplace ships a Claude Code skill that generates a Docker Compose stack (t8engine + rule-runner), the route TOML, optional rules, and the CA install instructions for your platform. From inside Claude Code:
/plugin marketplace add Inversed-Tech/t8
/plugin install t8engine-setup@t8
Then: "set me up with t8engine locally".
Docker Compose (manual)
Published images:
ghcr.io/inversed-tech/t8engine:v0.1
ghcr.io/inversed-tech/rule-runner:v0.1
Pass credentials via the T8_CONFIG environment variable (no volume mount required):
# Inline config from a file
T8_CONFIG=$(cat /path/to/config.toml) docker compose up -d t8engine
# Connect to a running control plane
CONTROL_PLANE_URL=http://backend:8000 docker compose up -d t8engine
T8_CONFIG_FILE is also supported for environments where a volume mount is available (e.g. production). T8 is available at http://localhost:1800.
Health check
curl http://localhost:1800/healthz
# {"status":"ok"}