5 · Assure
Observation tells you what the system is doing. Assurance answers the harder questions — the ones a risk owner, an incident review, or an auditor asks:
| Question | Answered by |
|---|---|
| What did it actually do, task by task, and what did each task cost? | The run ledger |
| Has it stopped working? | Liveness |
| Is it behaving differently than it used to — and why? | Behavioural drift |
| What exactly was it allowed to do on the day that run executed? | Contracts |
| Is it being used by whom, and for what, it was declared for? | Conformance |
| Is it healthy right now? | Worst-of health |
| Can you hand someone the evidence? | The Assurance Report |
Everything here attaches to the AI System — the same entity you created in Deploy — and most of it feeds itself from traffic you’re already sending. This page is the operational walkthrough; the AI System Assurance section explains the concepts from first principles and holds the full mechanics reference, one page per signal.
Day 0 — three switches
Section titled “Day 0 — three switches”Assurance mostly runs itself, but three things want a decision the day the system goes live:
-
Set the liveness expectation —
continuous,scheduled,sporadicoron_demand(right forsupport-copilot; a nightly pipeline wantsscheduled). This decision was already required by the gate; the detectors — silence, missed schedule, volume collapse, stall, hard-fail cliff — run against it, and Brutor can learn the cadence from history (sized on the p95 inter-run gap) instead of you guessing. -
Check the run ledger is filling — AI Estate → AI Systems → your system → Runs. If runs look like single requests, your run header isn’t reaching the gateway. Fix it now: every assurance signal downstream is built on runs.
-
Decide on automatic snapshots —
assurance_capture_interval_hourson the tenant captures immutable assurance reports on a schedule (off by default). If this system will ever face an auditor, turn it on: continuous post-market evidence produced by a machine beats someone remembering to click in December.
The first weeks — learning, then drift
Section titled “The first weeks — learning, then drift”For its first ~50 baseline-eligible runs the system is learning: no drift alerts, and the UI says “still learning this system’s normal” instead of showing an empty list that reads as a clean bill of health. Once baselines exist, drift detection compares current behaviour to them — robust statistics, sustained-change requirements (one bad afternoon is not drift) — and, crucially, ships the cause, not a p-value:
action_count up 180% since 14:00 on the 14th; the provider shipped a new point release of the underlying model at 13:52.
The gateway is the only component that sees both your traffic and the model registry, so it’s the only thing that can make that correlation. Candidate causes are checked in order — model change, tool change, composition change, input drift — and terminate at unexplained when nothing lines up. Severity ranks by consequence (effect size × volume × business impact), not statistical confidence.
Triage each finding: acknowledge, resolve, or not drift — the last one deliberately separate, because it’s evidence about the detector, and a detector that can’t be told it was wrong gets muted within a month.
Close the loop — response policies
Section titled “Close the loop — response policies”Detection can act. A response policy arms an automatic reaction on the system:
on: drift_class: behavioural severity_at_least: highthen: - set_autonomy: approval_requiredrequire_human_to_restore: trueThe runaway-refund scenario ends differently with this armed: cost-per-run drifts critical at 02:00, autonomy tightens to approval_required, and the 03:00 refund parks in an inbox instead of executing. The rails are deliberate — a policy never loosens, suspension is opt-in with a second acknowledgement, one action per cooldown, and restore is always human (automatic restore flaps). Every action and every considered-but-not-fired decision is recorded, as actor system:drift-response, with the blast radius previewed before you arm.
Contracts and conformance — prove the permissions
Section titled “Contracts and conformance — prove the permissions”The contract you minted at deploy keeps working after launch:
- Config changes → mint a new version; the stored diff separates widened from narrowed, and the approver sees exactly the diff they approved, forever.
- Conformance → because contracts are generated from live config, “is this system running configuration nobody approved?” is computable — and stated plainly on the Contracts panel.
- Declared vs observed → an undeclared client calling your system, a declared client that never calls, a granted resource never used (least-privilege finding): all reported once there’s enough traffic to mean something.
Before shipping a config change, replay the candidate contract against a stratified sample of real recorded runs — rarest-first sampling, so the suite includes the exhausted runs and rare tools a happy-path sample misses. outcome_changed (an action that used to succeed would now be denied) fails the gate; a new approval requirement is reported as policy_changed, stricter-not-broken.
Health — one number you can decompose
Section titled “Health — one number you can decompose”health = min(liveness, behaviour, reliability, cost, conformance)Worst-of, never an average — a system that is cheap, fast and dead is not 80% healthy. Only healthy renders green; learning and unknown are amber because they mean unmeasured, and unmeasured is exactly what an optimistic dashboard rounds up. The chain-integrity percentage sits beside the score so you know what it’s worth.
The Assurance Report
Section titled “The Assurance Report”One artifact per system, answering every question in the table at the top with evidence: identity and declared intent, contract history with approvers and hashes, drift events with causes and responses taken, run outcomes and cost per completed run, liveness record, replay results, conformance. The engineer opens it at 09:00; the auditor exports it in December — same artifact.
Snapshots are immutable (evidence that changes when the system does isn’t evidence), and every report ends with what it does not cover — chain integrity below 100%, guardrails not re-evaluated in replay, clients that never traversed the gateway. Captured reports feed ISO 42001 control coverage (A.6.2.4 performance evaluation, A.6.2.7 post-market monitoring) automatically.
curl http://localhost:5050/v1/admin/ai-systems/$G/assurance \ -H "Authorization: Bearer $ADMIN_TOKEN" # the live reportcurl -X POST http://localhost:5050/v1/admin/ai-systems/$G/assurance \ -H "Authorization: Bearer $ADMIN_TOKEN" # capture an immutable snapshotTamper evidence — proving the records themselves
Section titled “Tamper evidence — proving the records themselves”Everything above reasons over proxy_logs. That raises an obvious question an auditor
will ask: how do you know those rows say what they said when they were written?
Every audit row carries a SHA-256 covering its own contents and its predecessor’s
hash, so altering any row breaks every hash after it. Chains are per proxy process —
a restart begins a new one, which is expected. Batches of rows are signed with Ed25519
into audit_chain_checkpoints, because hashes alone only detect edits: anyone able to
rewrite the rows could recompute the chain. A signature is what makes that require a key.
Verify from System → Audit Chains in the Admin Console, or:
curl -X POST "http://localhost:5050/v1/admin/audit/chains/$CHAIN/verify" \ -H "Authorization: Bearer $ADMIN_TOKEN"Six verdicts, because “not verified” covers situations you would act on differently:
| Verdict | What it claims |
|---|---|
verified |
Hashes link and a checkpoint signed by a trusted key covers them. The only verdict asserting integrity. |
unattested |
Hashes link; nothing signed them. No signing key configured — see BRUTOR_AUDIT_SIGNING_KEY. |
gap |
Sequence numbers missing: rows were deleted. |
broken |
A row no longer hashes to its stored value. |
forged |
A signature verifies against a key this deployment never trusted. Ranked worst — a hash break can be corruption, an unauthorised attestation is deliberate. |
unchained |
No chain columns. Rows written before tamper-evidence was enabled — not a pass and not a failure. |
Consistent with the rest of assurance: absence of evidence is never reported as evidence of integrity, and a check that could not run returns an error rather than a clean result.
Scheduled audit exports to S3 carry the same property outward. Each export writes a
<object>.manifest.json sidecar with a SHA-256 over the delivered bytes — so a recipient
verifies with sha256sum alone — signed by the same key. The manifest also records
chain_columns_included, because a field allowlist that strips row_hash silently makes
an export unverifiable and the recipient cannot tell from the file.
End of the journey — and the loop back
Section titled “End of the journey — and the loop back”Retirement is a gate too: active → deprecated requires naming a successor (or stating there is none), and deprecated → retired requires 30 quiet days. Nothing simply disappears with its history.
More often, assurance sends you backwards around the loop: a drift finding becomes a governance change (Govern), tested by replay, minted as a contract version, observed (Observe) — one AI System, one thread of evidence, the whole way around.

