Routes and Consumer Groups
Path- and header-based route matches and API key-based consumer groups select policy chains and request-count limits for supported gateway requests.
Routes
The live gateway uses routes: to select a per-route policy chain for Chat Completions (POST /v1/chat/completions) and Responses (POST /v1/responses) requests. Other request families, including Embeddings, do not currently run route resolution. Without a matching route chain, the request uses the global chain.
The schema also accepts route-level upstream and strip_path fields, but the live request handlers do not consume either field. Configure provider selection under providers: and do not rely on routes to change the provider or forwarded path.
Basic path routing
pack:
name: config-routes-and-consumers-routes-1
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-5.4-mini
secret_key_ref:
env: KEEPTRUSTS_OPENAI_API_KEY
policies:
chain:
- audit-logger
- prompt-injection
- pii-detector
policy:
audit-logger: {}
prompt-injection: {}
pii-detector:
action: redact
routes:
- name: chat
path: "/v1/chat/completions"
match: exact
chain:
- prompt-injection
- pii-detector
- name: responses
path: "/v1/responses"
match: exact
chain:
- audit-logger
Full route reference
pack:
name: config-routes-and-consumers-routes-2
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-5.4-mini
secret_key_ref:
env: KEEPTRUSTS_OPENAI_API_KEY
policies:
chain:
- audit-logger
- prompt-injection
- pii-detector
policy:
audit-logger: {}
prompt-injection: {}
pii-detector:
action: redact
routes:
- name: engineering-chat
path: "/v1/chat/completions"
match: exact
methods:
- POST
headers:
X-Team: engineering
priority: 10
chain:
- prompt-injection
- pii-detector
Route fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | yes | — | Unique route identifier used by schema validation and diagnostics |
path | string | yes | — | URL path pattern to match |
match | string | no | prefix | prefix or exact match |
methods | string[] | no | all | HTTP methods this route applies to |
headers | map | no | — | Header conditions (all must match) |
priority | integer | no | 0 | Higher priority wins when multiple routes match |
strip_path | boolean | no | false | Parsed and linted, but not applied by the live request handlers |
upstream | string | no | — | Parsed and linted, but not used for live provider selection |
chain | ChainEntry[] | no | — | Per-route policy chain (overrides global) |
Route resolution
For Chat Completions and Responses requests, the gateway:
- Collects routes whose normalized path, method, and headers all match.
- Sorts them by
prioritydescending. For equal priorities, the first declared route wins. - Uses the matched route's
chainwhen one is configured. - Uses the global chain when no route matches or the matched route omits
chain.
Both live handlers invoke route resolution with POST, so a route restricted to another method does not match. Route matching does not alter provider routing or the forwarded path.
Separate chains per endpoint
pack:
name: multi-endpoint
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-5.4-mini
secret_key_ref:
env: KEEPTRUSTS_OPENAI_API_KEY
routes:
- name: chat-secured
path: "/v1/chat/completions"
match: exact
chain:
- prompt-injection
- pii-detector
- quality-scorer
- name: responses-audited
path: "/v1/responses"
match: exact
chain:
- audit-logger
policies:
chain:
- audit-logger
- prompt-injection
- pii-detector
- quality-scorer
policy:
audit-logger: {}
prompt-injection: {}
pii-detector:
action: redact
quality-scorer:
thresholds:
min_aggregate: 0.8
Method conditions
The schema permits a methods list, but the two handlers that perform route resolution are both POST endpoints. Use POST or omit methods; a route for GET /v1/models, for example, is accepted by the parser but is never resolved by that handler.
Header-based routing
pack:
name: config-routes-and-consumers-routes-5
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-5.4-mini
secret_key_ref:
env: KEEPTRUSTS_OPENAI_API_KEY
policies:
chain:
- audit-logger
- prompt-injection
- pii-detector
- dlp-filter
policy:
audit-logger: {}
prompt-injection: {}
pii-detector:
action: redact
dlp-filter:
action: block
routes:
- name: engineering-route
path: "/v1/chat/completions"
match: exact
headers:
X-Team: engineering
chain:
- audit-logger
priority: 10
- name: compliance-route
path: "/v1/chat/completions"
match: exact
headers:
X-Team: compliance
chain:
- prompt-injection
- pii-detector
- dlp-filter
priority: 10
- name: default-route
path: "/v1/chat/completions"
match: exact
chain:
- audit-logger
priority: 0
Parsed-only routing fields
upstream and strip_path are present in the route schema and lint rules, but the live Chat Completions and Responses handlers use only the resolved chain. The server does not read Route.upstream and does not call the route path-rewrite helper. Leave both fields unset until runtime support is implemented.
Consumer groups
The consumer_groups: section identifies API consumers by a presented key. For Chat Completions and Responses requests, a matched group can replace the policy chain and enforce an in-memory max_requests window on that gateway instance.
The schema also accepts rate_limit.max_tokens and group-level upstream, but the live handlers do not consume those values. Do not rely on them for token quotas or provider selection.
Basic consumer group
consumer_groups:
key_header: "Authorization"
groups:
- name: "enterprise"
api_keys:
- "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
rate_limit:
max_requests: 1000
window_seconds: 60
Full consumer group reference
consumer_groups:
key_header: "Authorization" # header to extract the API key from (default)
groups:
- name: "enterprise"
api_keys: # SHA-256 hashes of permitted keys
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
rate_limit:
max_requests: 1000
window_seconds: 60
chain: # per-group chain override (replaces global)
- "audit-logger"
- name: "free-tier"
api_keys:
- "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
rate_limit:
max_requests: 10
window_seconds: 60
Consumer group fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
key_header | string | no | "Authorization" | Request header containing the API key |
groups[].name | string | yes | — | Group name for logging and diagnostics |
groups[].api_keys | string[] | yes | — | SHA-256 hex hashes of valid API keys |
groups[].rate_limit.max_requests | integer | no | — | Live in-memory request limit per group and gateway instance for Chat Completions and Responses |
groups[].rate_limit.max_tokens | integer | no | — | Parsed, but not enforced by the live handlers |
groups[].rate_limit.window_seconds | integer | yes (if rate_limit) | — | Rate limit window |
groups[].chain | ChainEntry[] | no | — | Override the policy chain for this group |
groups[].upstream | string | no | — | Parsed, but not used for live provider selection |
How key matching works
- The gateway extracts the value from
key_header - When
key_headerisAuthorization, strips theBearerprefix if present - Computes the SHA-256 hash
- Looks up the hash in the consumer group index
- For Chat Completions and Responses, applies the group's
max_requestslimit and uses itschainwhen configured - If no group matches, uses the route chain when configured, otherwise the global chain
For any other request family, the current handlers do not resolve consumer groups.
Generating key hashes
echo -n "my-secret-api-key" | shasum -a 256 | cut -d' ' -f1
# → 325ededd6c3b9988f623c7f964abb9b016b76b0f8b3474df0f7d7c23b941381f
Tiered access example
pack:
name: "tiered-access"
version: "1.0.0"
enabled: true
providers:
targets:
- id: "openai-primary"
provider: "openai"
model: "gpt-5.4-mini"
secret_key_ref:
env: "KEEPTRUSTS_OPENAI_API_KEY"
consumer_groups:
key_header: "Authorization"
groups:
- name: "enterprise"
api_keys:
- "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
rate_limit:
max_requests: 5000
window_seconds: 3600
chain:
- "audit-logger"
- name: "professional"
api_keys:
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
rate_limit:
max_requests: 500
window_seconds: 3600
- name: "free-tier"
api_keys:
- "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
rate_limit:
max_requests: 20
window_seconds: 3600
chain:
- "prompt-injection"
- "pii-detector"
- "quality-scorer"
policies:
chain:
- "prompt-injection"
- "pii-detector"
- "quality-scorer"
- "audit-logger"
Combining routes and consumer groups
For Chat Completions and Responses, the gateway resolves both a matching route and a consumer group. A configured consumer-group chain takes precedence over a route chain; a route chain takes precedence over the global chain. Consumer and route upstream values do not participate in provider selection.
pack:
name: "combined-routing"
version: "1.0.0"
enabled: true
providers:
targets:
- id: "openai-primary"
provider: "openai"
model: "gpt-5.4-mini"
secret_key_ref:
env: "KEEPTRUSTS_OPENAI_API_KEY"
routes:
- name: "responses-audited"
path: "/v1/responses"
match: exact
chain:
- "audit-logger"
consumer_groups:
groups:
- name: "internal"
api_keys: ["dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"]
rate_limit:
max_requests: 10000
window_seconds: 3600
chain:
- "prompt-injection"
- "pii-detector"
policies:
chain:
- "prompt-injection"
- "pii-detector"
- "quality-scorer"
- "audit-logger"
Next steps
- Providers Configuration — Define upstream targets
- Config Rate Limits — Global and per-scope rate limiting
- Policy Controls Catalog — Available chain policies
- Security and Network Configuration — Gateway IP allowlisting