Evidence-required mode, policy-attributed artifacts, and an honest cache key

2026-08-07

routersecuritygovernanceapi

LOCKSTEP TRACEABILITY MATRIX --- api_endpoints: ["GET /v1/security/processor-facts", "POST /v1/security/processor-facts/validate"] sdk_methods_updated: [ "security.processorFacts()", "security.validateProcessorFacts()", "security.processor_facts()", "security.validate_processor_facts()", ] mcp_tools_updated: ["none"] ---

What We Built

Four things that turn the data-protection layer from "resolved and recorded" into "enforced and provable", plus the admin surface that keeps its inputs honest.

The exact cache no longer answers for a prompt it did not see. promptCacheMiddleware runs before the handler's before_prompt_build hooks, so when a prompt-mutating plugin was registered the cached hash did not describe the request that would actually be sent — a hit could replay output generated under different plugin context. The cache now bypasses entirely when any such hook is registered (hasGlobalHooks), rather than answering from a hash that does not represent the request. Separately, the hash now covers _augmentedMessages when the agent-context injector rewrote them, instead of the raw body.

Evidence-required mode. The audit chain is fail-open by design, which is correct — a chain-write failure must never fail a completion — but it means "every request produces proof" was not a claim the platform could make. evidenceMode now has teeth:

  • normal — the completion succeeds; coverage is reported as evidence_unavailable.
  • required — a content-free receipt must commit, or the request fails with 503.

503 rather than 403 is deliberate: the request was permissible, the platform simply could not produce the proof it owes, and retrying may succeed.

Streaming needed its own protocol. A stream commits its 200 and first token long before an outcome exists, so a single end-of-request receipt cannot gate success. A _provisional_ gate runs before the first byte (refusing required mode when the evidence plane is unavailable, while a status code is still negotiable), and the _final_ receipt commits before [DONE]. If that final commit fails, a structured evidence_failure event is emitted in-band — the status can no longer be changed, so the signal travels in the stream.

Governance artifacts now attribute constraints to their origin. defaultArtifactControls emitted three hardcoded SOC 2 controls. It now also projects the enforced data-protection posture — retention ceiling → GDPR Art. 5(1)(e), training prohibition → Art. 28, region limits → Art. 44, evidence mode → EU AI Act Art. 12 — with satisfiedBy naming the composition origins. The artifact says "the tenant's policy required this, the API key tightened it, and the router held it" rather than an unattributed claim.

Processor facts got a read-back and a dry run. Facts are legal assertions the platform never makes on a customer's behalf, and the parser drops anything malformed so a bad assertion can never become a permissive one. Without a read-back surface that is indistinguishable from a correct configuration.

Why It Matters

Two of these close the gap between what the system records and what it can prove. A cache that answers for a prompt it never saw makes the decision trace describe the wrong request. An audit chain that silently fails makes "we have evidence" a statement about the happy path.

The third is the competitive one. Portkey and OpenRouter ship controls; Credo AI ships evidence with no runtime. An artifact that names which policy source imposed each constraint, generated from inside the routing decision, is the thing neither side produces.

How It Works

Evidence classification is deliberately three-valued:

export type EvidenceCoverage = "committed" | "evidence_unavailable" | "not_configured";

A deployment with no evidence store is an operator choice, not a per-request failure — conflating the two would make every self-hosted request look like a broken audit chain. But not_configured does not satisfy required mode either; it fails closed.

The evidence_unavailable signal is surfaced out-of-band — a response header plus an independent counter — because it must not live only in the store that just failed to accept it.

The artifact projection claims only what was enforced:

if (!trace || trace.enforced !== true) {
  return [];
}

A tenant who declared nothing produces no controls. The artifact never implies a framework was satisfied merely because it was named.

The Numbers

  • 2 new API routes, in lockstep across TS SDK, Python SDK (sync + async), OpenAPI

(660 → 662 routes) and the capability registry.

  • 4 enforcement boundaries now live: final payload, cache/derivation, actually-selected

endpoint, persistence/evidence.

  • Route-count delta verified through pnpm build, which regenerates the OpenAPI spec and

llms-full.txt from the capability registry.

Competitive Edge

The routing engine is the only place where a policy can be enforced _and_ witnessed in the same step. Everything here is built on that: the receipt commits inside the request, the artifact is derived from the decision that request actually made, and the controls cite the source that imposed them. A governance platform outside the request path can assert none of it; a gateway without evidence can prove none of it.

Lockstep Checklist

  • [x] API Routes: GET /v1/security/processor-facts,

POST /v1/security/processor-facts/validate — both security.read, registered in _registry.ts.

  • [x] TS SDK: security.processorFacts(), security.validateProcessorFacts().
  • [x] Python SDK: processor_facts() / validate_processor_facts(), sync + async.
  • [x] MCP Schemas: not agent-facing — these are operator/compliance surfaces.
  • [x] Master Record: docs/architecture/master-capability-record.mdx — "Evidence receipts"

added to Tier 1.

Known limits

  • Validation is structural, not legal. The dry run catches unparseable entries, facts that

attest nothing, and missing evidenceRef. It cannot tell whether a customer actually holds the agreement they assert — nothing can, from inside the process.

  • The provisional streaming receipt checks availability, not commitment. It verifies the

evidence plane is reachable before the first byte; it does not write a provisional row. A write that fails mid-stream is caught by the final receipt and reported in-band.

  • Live verification still outstanding. Everything here is unit-verified. The cache bypass

and evidence-mode paths in particular should be exercised against the deployed service before they are relied on.