Gateway Service
Self-hosted /v1/messages inference gateway and Cowork integration. Routes across Anthropic, OpenAI, and Gemini with identity propagation and a signed audit log.
On this page
TL;DR — systemprompt.io exposes a
/v1/messagesendpoint that is wire-compatible with Anthropic's API, plus a/v1/auth/bridge/*family for the credential helper. Point Claude Cowork's third-party inference at it and every prompt, tool call, and cost line lands in your own database. Thesystemprompt-bridgebinary is the one-click companion that mints credentials for the helper script across personal, team, and enterprise deployments — same binary, three tiers of identity.
Why It Matters
Anthropic documents Claude Cowork running on customer infrastructure in its third-party platform documentation (the feature matrix, installation and configuration, and the extension surfaces article). Inference can route through Bedrock, Vertex AI, Azure Foundry, or any LLM gateway exposing /v1/messages. The last option is what makes identity propagation, cross-provider routing, and audit lineage possible — but only if that gateway exists.
The gateway service is that endpoint. It runs inside the same systemprompt.io binary as the governance pipeline, the MCP registry, and the audit log — so every Cowork request inherits the same policy enforcement and the same trace_id lineage as everything else.
Architecture
Claude Cowork
│
│ 1. runs systemprompt-bridge (Credential helper script)
▼
systemprompt-bridge ── probes mTLS → session → PAT, picks the first available
│
│ 2. POST /v1/auth/bridge/{mtls|session|pat}
▼
systemprompt.io gateway
│
├── mints short-lived JWT (UserId, SessionId, ClientId, TenantId)
├── returns {"token", "ttl", "headers"}
│
│ 3. Cowork issues /v1/messages with bearer JWT + merged headers
▼
/v1/messages middleware
│
├── governance pipeline (scope, secrets, blocklist, rate limit)
├── resolve routing rule → provider
├── forward to Anthropic | OpenAI | Gemini
├── stream response back
└── write audit row (prompt, response, tokens, cost) keyed by trace_id
Every inference request carries the canonical headers core uses everywhere else (crates/shared/identifiers/src/headers.rs):
| Header | Typed ID | Meaning |
|---|---|---|
x-user-id |
UserId |
Who made the request |
x-session-id |
SessionId |
Session this turn belongs to (sess_<uuid>) |
x-trace-id |
TraceId |
Per-request correlation ID; ties inference to tool calls and MCP invocations |
x-client-id |
ClientId |
Which client emitted the request |
x-call-source |
SessionSource |
Channel that issued the call (bridge, api, cli, web, oauth, mcp) |
x-policy-version |
(plain header) | Hash of the policy bundle applied at JWT mint time; unversioned when no policy bundle has been published |
The gateway validates these match the JWT claims before forwarding. The provider never sees them; they are stripped at the outbound boundary.
Endpoints
POST /v1/messages
Anthropic-compatible inference endpoint. Accepts the same request body Cowork and Claude Code emit, streams responses via SSE, and preserves anthropic-version, anthropic-beta, and tool-use headers end-to-end. A sibling POST /v1/responses route accepts the OpenAI Responses wire format for clients that speak it.
Auth: Authorization: Bearer <jwt> (minted by the helper chain). Unauthenticated requests return 401.
Routing is resolved via the AI service configuration (services/ai/) plus gateway routing rules. See AI Services for provider configuration.
POST /v1/auth/bridge/pat
Personal access token exchange. Returns {"token", "ttl", "headers"}.
Used by systemprompt-bridge's PAT provider. The PAT is long-lived and stored with 0600 permissions in the helper's config directory (systemprompt-bridge login prints the exact path).
POST /v1/auth/bridge/session
Team-tier exchange. Exchanges a one-shot session code (issued via systemprompt admin bridge issue-code) for a short-lived JWT scoped to the user the session belongs to. A companion POST /v1/auth/bridge/session-pat route mints a durable PAT from an active session.
POST /v1/auth/bridge/mtls
Enterprise-tier exchange. Requires an mTLS client certificate (device identity) enrolled via systemprompt admin bridge enroll-cert. Returns a JWT carrying the certificate fingerprint as a claim. The device cert is provisioned via MDM.
GET /v1/auth/bridge/capabilities
Unauthenticated probe. Returns {"modes": ["pat", "session", "mtls", "oauth-client"]} — the auth modes this gateway accepts. systemprompt-bridge calls this to validate the gateway URL.
The systemprompt-bridge Helper Binary
Cowork's third-party inference panel has a field named Credential helper script — an absolute path to an executable that prints the bearer token (or {"token", "headers"} JSON) to stdout. Cowork caches the result for the TTL, re-invokes on expiry. The spec is strict: stdout must be the credential and nothing else; any banner, prompt, or log line breaks it.
systemprompt-bridge is the universal implementation. One binary, three tiers of identity, selected by capability probe:
- mTLS — if an enrolled device cert is available, it wins.
- Session — if a bridge session is available, it's next.
- PAT — if neither is present, fall back to the personal access token.
Install it once. Credentials are layered on as the deployment matures. Cowork sees the same stdout contract every time.
Install and enroll
# Install the helper and wire up the local host integrations
systemprompt-bridge install
# Enroll against your gateway (stores the credential with 0600 permissions
# and prints the config + secret file paths)
systemprompt-bridge login
# Verify the enrollment and stdout contract
systemprompt-bridge status
systemprompt-bridge doctor
The helper caches minted JWTs at ~/.cache/systemprompt-bridge/cache.json (0600) and serves them without a network round trip until the TTL expires.
Point Cowork at it
In Cowork: Help → Troubleshooting → Enable Developer mode, then Developer → Configure third-party inference:
- Gateway URL: your systemprompt.io base URL (e.g.
http://localhost:8080) - Gateway auth scheme:
bearer - Credential helper script: the absolute path to
systemprompt-bridge - Leave static key fields blank. The helper overrides them anyway.
- Toggle Skip login-mode chooser on — users should not see Anthropic's sign-in screen.
Provider Routing
The gateway forwards /v1/messages to a configured provider — Anthropic direct, OpenAI, or Google Gemini — based on the AI service configuration in services/ai/ and gateway routing rules.
Routing resolves by:
model— route by requested model pattern (e.g.claude-*→ a provider + upstream model)default_provider— the catch-all route when no pattern matches
See AI Services for provider configuration and the systemprompt admin config provider / admin config catalog commands.
Bridge Manifest for Cowork
Cowork's Extend-on-third-party-platforms model lets admins specify a remote MCP allowlist and tool policies (allow / ask / block). The gateway exposes a signed manifest route that feeds it:
GET /v1/bridge/manifest— returns the user's entitled MCP servers and plugin set as an ed25519-signed manifest. Thesystemprompt-bridge synccommand deposits the resulting plugins into Cowork'sorg-plugins/mount.
The route keys off the same JWT the helper minted. See MCP Service for registry configuration and Claude Cowork Integration for the gateway + plugin + allowlist story in one place.
Audit Trail
Every /v1/messages request produces one audit row with:
- The
trace_id(also emitted to Cowork via the stream) - Full prompt and response content (encrypted at rest with the configured key)
- Token counts and microdollar cost computed from the provider's pricing
- Resolved
UserId,SessionId,ClientId,TenantId - The MCP server ID and tool name for any follow-up tool calls, linked by the same
trace_id
Forward the JSON stream to Splunk / ELK / Datadog / Sumo Logic via the analytics service, or query it directly with systemprompt analytics requests list.
CLI
# Issue a one-shot session exchange code for the bridge helper
systemprompt admin bridge issue-code
# Enroll a device certificate fingerprint for a user (mTLS tier)
systemprompt admin bridge enroll-cert
# List active bridge sessions (recent heartbeats)
systemprompt admin bridge list
# Rotate the ed25519 manifest signing seed
systemprompt admin bridge rotate-signing-key
# Inspect gateway traffic
systemprompt infra logs request list --limit 20
systemprompt analytics requests list
Verified against a live instance
Tested against a local gateway at http://localhost:8080 with systemprompt-bridge (debug build, PAT provider). The capability probe returned the supported modes, POST /v1/auth/bridge/pat returned a valid JWT, and the helper's stdout contract held: a single JSON document, first byte {, empty stderr on the happy path. The cache persisted at ~/.cache/systemprompt-bridge/cache.json (0600) and was served without a network round trip on subsequent invocations; a bad PAT and an unreachable gateway both produced 0 stdout bytes and a single-line diagnostic on stderr.