A-3 — trust envelope load-bearing for routing: tier/scope/xdr_risk/anomaly gates

2026-05-09

routertrust-envelopegateway

LOCKSTEP TRACEABILITY MATRIX --- api_endpoints: ["none — internal routing change, no new routes"] sdk_methods_updated: ["none"] mcp_tools_updated: ["none"] ---

What We Built

applyTrustGates — the single function that translates trust-envelope claims into routing constraints. It runs BEFORE bandit selection in model-router-select.ts so every downstream strategy (quality, price, latency, Thompson sampling) operates on a trust-bounded candidate pool.

Signal precedence (first match wins, per Justin 2026-05-09):

  1. br_trust.xdr_risk >= 0.7 → force-downgrade to "restricted" (price strategy + cost cap). External XDR signal is sourced outside BR, highest reliability.
  2. br_trust.anomaly_score >= 0.8 → downgrade nominal tier by one level. In-process detector, evaluated last.
  3. Nominal tier (restricted/bronze) → price strategy + cost cap for restricted; price strategy only for bronze.

Scope filtering: br_scope.providers (non-empty) restricts to those providers; br_scope.models (not "*") restricts to listed model IDs.

Three modes:

  • "off" (default): pass-through, no filtering — A-2 behavior preserved.
  • "warn": compute gates, log when a constraint would have fired, return full candidate set.
  • "enforce": apply filtering + strategy overrides before bandit selection.

Wiring:

  • gateway.envelope.routing.mode config key → ModelRouterDeps.envelopeRoutingMode → passed to applyTrustGates in resolveEndpoint.
  • RouteCompletionParams.trustEnvelope → set from c.get("_trustEnvelopeDecoded") in the non-streaming completions handler.
  • boot-router.ts reads the config key and passes it to initModelRouter.

Lockstep Checklist

  • [x] TypeScript SDK: no change (routing behavior, not API surface)
  • [x] Python SDK: no change
  • [x] MCP tools: no change
  • [x] Config schema: gateway.envelope.routing.mode added to zod-schema.ts + types.gateway.ts
  • [x] OpenAPI: no change
  • [x] Ship log: this entry

How to activate in production

  1. First activate A-2 synthesis: gateway.envelope.synth.mode = "audit-only"
  2. Then enable routing gates: gateway.envelope.routing.mode = "warn" (observe for 1 week)
  3. Once warn logs look correct: gateway.envelope.routing.mode = "enforce"

The streaming path (routeCompletionStream) does not yet pass trustEnvelope — add in A-3.1 if streaming enforcement is needed before A-4.