Usage¶
SecRouter speaks the OpenAI chat-completions API, so most clients work by changing one setting: the base URL.
Authenticate¶
When security is enabled, every request (except /health) must carry a bearer JWT from your IdP:
Authorization: Bearer <token>
Interactive users sign in through your IdP and the client forwards the access token.
Machine clients (CLI, pipelines) use the OIDC client-credentials grant to obtain a token.
A request with no token, an invalid token, or one missing MFA is rejected with 401.
Make a request¶
curl https://secrouter.example.url/v1/chat/completions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "auto",
"messages": [{"role": "user", "content": "Summarize this contract clause…"}]
}'
Use "model": "auto" to let the classifier choose, or name a specific model to pass through (subject to your allowlist).
Streaming works as usual — set "stream": true and read the SSE response.
Smart routing & overrides¶
With auto, a weighted classifier scores each request and routes to the cheapest capable tier. Override it inline when you know better — the prefix is stripped before the model sees it:
/simple What's 2+2?
/max Analyze this distributed system for race conditions
[complex] Refactor this module to use dependency injection
deep mode: Why does this recursive CTE produce duplicates?
Aliases |
Tier |
|---|---|
|
SIMPLE |
|
MEDIUM |
|
COMPLEX |
|
REASONING |
Embeddings¶
POST /v1/embeddings is governed exactly like chat — same OIDC auth, per-user model policy, classification clearance, deny-by-default egress, quota, and per-user cost accounting — so RAG pipelines run through the control plane instead of around it.
curl https://secrouter.example.url/v1/embeddings \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "model": "auto", "input": "text to embed" }'
"model": "auto" uses the configured embeddings.default; or name an embedding model directly (subject to your allow-list). Register embedding models from the console’s add endpoint wizard by ticking embedding on the discovered models.
MCP tool gateway¶
Point an MCP client (your IDE, an agent, or a CLI) at /mcp with the same OIDC bearer, and SecRouter brokers every tools/list and tools/call to your registered in-boundary MCP servers under the same governance as chat — so agentic tool use runs through the control plane instead of around it:
Deny-by-default tools. A principal sees and can call only the tools granted by
policy.allowedTools(namespacedserver/tool, with aserver/*wildcard). No grant ⇒ no tools, andtools/listis filtered so clients never even see unsanctioned tools.Classification-gated. A
tools/callis refused unless the request’s data classification is one the destination server is authorized to receive.Audited, CUI-safe. Every call is a
tool.call(ortool.deny) audit event recording the server, tool, byte counts, and a SHA-256 of the arguments — never their contents.
Register servers under security.mcp (see Configuration); grant tools per group/user on the console’s Users tab (Allowed tools). The gateway is off unless security.mcp.enabled.
curl https://secrouter.example.url/mcp \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }'
Endpoints¶
Endpoint |
Auth |
Description |
|---|---|---|
|
user |
Route & forward (OpenAI-compatible) |
|
user |
Governed embeddings (OpenAI-compatible) |
|
user |
Governed MCP tool gateway (off unless |
|
user |
List configured models |
|
user |
Your own token / cost usage |
|
open |
Liveness probe |
|
bearer / none |
Prometheus metrics (off unless |
|
open shell |
Admin web console (OIDC PKCE login) |
|
admin |
Org usage + policy/model config |
|
admin |
Operations |
See your usage¶
Any user can check their own spend:
curl -H "Authorization: Bearer $TOKEN" https://secrouter.example.url/v1/usage
{
"principal": "alice@example.url",
"usage": { "last24h": { "requestCount": 12, "inputTokens": 30400, "costUsd": 0.21 } },
"budgets": [{ "window": "day", "maxCostUsd": 25 }]
}
When a budget or rate limit is exceeded, requests return 429 until the window rolls over.
Admin console¶
Browse to /admin and sign in (OIDC PKCE). Admins can:
Monitor per-user / model / day usage and cost, plus provider health — the circuit-breaker state of each upstream (healthy / open / half-open), so a failing endpoint is visible at a glance.
Configure group and per-user policies and tier→model routing — changes are written to an audited overrides layer and applied live.
Add model endpoints (below) — register a local / on-prem model with a guided wizard.
Review the hash-chained audit trail.
Add a local or on-prem endpoint¶
The Models tab has a guided wizard for registering a self-hosted, OpenAI-compatible model server (vLLM, Ollama, TGI, LM Studio, or any in-boundary endpoint):
Connect — enter the base URL (e.g.
http://llm.internal:8000/v1) and auth (an env-var name, a one-time token used only for the test, or none — many on-prem servers need no key), then Test endpoint. SecRouter probes it and lists the models it serves.Select & price — pick the models to register and set their
$/M-token rates (default0for self-hosted compute, so per-user budgets and cost reports still apply). Optionally make one the primary for a routing tier.Set egress — choose the data classifications this destination may receive. This writes a deny-by-default egress rule, so the endpoint is only reachable for those classifications.
Validate → Apply → Reload — SecRouter validates the change, writes it to the config file (atomically, with a
.bakbackup — the file stays your change-controlled source of truth), and you apply it with a no-downtime Reload or a full Restart.
Every step is admin-only and audited. The probe is restricted to in-boundary hosts by default (set SECROUTER_PROBE_ALLOW_HOSTS to allow others). Because a new endpoint always comes with an explicit, validated egress rule, the deny-by-default CUI boundary is preserved.
Client integration (OpenAI SDKs)¶
from openai import OpenAI
client = OpenAI(
base_url="https://secrouter.example.url/v1",
api_key=token, # your OIDC access token
)
resp = client.chat.completions.create(
model="auto",
messages=[{"role": "user", "content": "Hello"}],
)