Skip to content

Framework registry & evidence bases

A framework in Brutor is data, not code: a catalog of dated obligations, each shown against evidence statements that name a resolver from one shared library. The same resolver with the same parameters gives the same answer under every framework that cites it — one evidence log, many audits.

Framework Id Kind Verified against instrument Obligations
EU AI Act eu-ai-act regulation yes 28
ISO/IEC 42001:2023 iso-42001 standard yes 15
SOC 2 soc2 attestation yes 10
HIPAA Security Rule hipaa regulation yes 7
GDPR gdpr regulation yes 8
DORA dora regulation no 4
NIS2 nis2 regulation no 2
NIST AI RMF 1.0 nist-ai-rmf voluntary no 6
Colorado AI Act colorado regulation no 4
Korea AI Framework Act korea regulation no 3
Brazil PL 2338 brazil regulation (bill) no 1

A catalog with verified_against_instrument: false renders a “verify against the published instrument before relying on this” banner in the console and on its docs page.

A framework carries: id, title, kind (regulation | standard | attestation | voluntary), jurisdiction, reference, versions[] (catalog version with effective range), a role vocabulary and a classification vocabulary (and the profile fields they are read from), obligations[], deadline_rules (incident classification → time), retention_floors (classification → days, * = any), verified_against_instrument, and an optional tag_key linking it to compliance tagging.

Each obligation carries key, title, reference, applies_from (per catalog version), summary, applicability filters (classifications, roles, neutral-core flags, GPAI track), a needs_review note where the platform cannot decide alone, an obligation-level limit, aliases for renamed keys — and evidence[]:

@dataclass(frozen=True)
class EvidenceSpec:
key: str
statement: str
basis: Literal["recomputed", "judged", "indicator", "declared"]
resolver: str # shared resolver id, or judge.<template>
params: Mapping[str, Any] # e.g. {"floor_days": 183}, {"tag": "hipaa.phi"}
applies_to_kinds: FrozenSet[str] | None = None
limit: str | None = None # what this statement does NOT show

A static test asserts that every resolver named in every catalog exists, and every catalog date is pinned by a test.

Enabling a framework for a tenant (Compliance → Frameworks, or PATCH /v1/admin/compliance/frameworks/{id} with {"enabled": true}) is the same switch as adding its tag key to the tenant’s tagging profile.

Every AI System has one compliance profile (Compliance → Profiles, or PUT /v1/admin/compliance/ai-systems/{id}/profile): a neutral core plus a frameworks block keyed by registry id.

{
"core": {
"operator_role": "builder",
"jurisdictions": ["EU", "US"],
"entities": { "provider": "Example Insurance SE", "deployer": "Example Insurance SE" },
"dates": { "put_into_service": "2026-03-01" },
"external_registrations": [{ "framework_id": "eu-ai-act", "registry": "EU database", "id": "EUDB-0000" }],
"sensitivity": { "personal_data": true, "special_category": false, "phi": false, "financial": true, "minors": false },
"interacts_with_natural_persons": true,
"generates_synthetic_content": false,
"automated_decisions_about_persons": true,
"two_phase_actions": null,
"inference_records": null,
"salt_digests": null,
"oversight_assignment": "Claims operations duty lead",
"notes": "Scope confirmed by the AI governance board, 2026-08"
},
"frameworks": {
"eu-ai-act": { "role": "provider_and_deployer", "risk_tier": "high", "annex": "annex_iii", "qms_reference": "QMS-7" },
"gdpr": { "role": "controller", "dpbd_reference": "DPbD-2026" },
"soc2": { "in_scope": true }
}
}

operator_role is one of builder, operator, vendor_of, reseller. The EU AI Act block is validated strictly (role, risk_tier, in_scope, annex, annex_iii_area, conformity_assessment and the declared references); every other block is validated against its catalog’s role and classification vocabularies, and otherwise holds the fields that catalog’s DECLARED statements cite (scalars or short lists). The EU AI Act role and risk_tier are written through to the resource group’s legacy columns. null switches mean “default: on when high-risk in any framework” (options).

Every save seals an ai.brutor.declaration record whose payload digest covers the saved profile, so each DECLARED statement cites a sealed declaration. A save the core cannot seal is still saved, and the response says sealed: false with the error.

An obligation’s applicability per system is applies, not_applicable or undetermined — an undeclared role, classification or flag can never rule an obligation out.

Basis How it is established Can be met?
recomputed derived by a resolver from the tenant log, the run ledger and configuration yes — met, not_met, not_evaluable, undetermined
judged a tier-B model template over a deterministic, disclosed sample, stated with human agreement yes, and always shown with its agreement; never summed with recomputed
indicator movement over time, never a threshold never — status indicator
declared an operator statement, sealed but not checked never — status declared

Statement statuses: met, not_met, not_evaluable (nothing to measure), undetermined (a declaration or input is missing, or could not be established), indicator, declared. An obligation cell in the matrix shows the worst status of its statements, ranked not_met < undetermined < not_evaluable < met < indicator < declared, beside per-basis counts that are never merged.

Every StatementResult carries key, statement, basis, resolver, params, status, n, m, detail, limit, citations (up to 20 record ids), catalog_version, window_start, window_end, evaluated_at, facts (sub-counts, never merged into n/m) and refs (non-record pointers such as evidence pointers, tree heads, contracts). The default window is 30 days.

Resolvers are framework-neutral and live in the Control Plane (app/services/compliance/resolvers/). They never return met on missing inputs, count n of m rather than percentages, and never raise — an internal failure is not_evaluable with the reason.

Resolver Basis Computes Does not show
records.every_verdict_sealed recomputed governed actions in the window that should carry a sealed record (the consequential rule) and do — n of m; refusals sealed of refusals; backfilled records reported separately actions not routed through the gateway; integrity is not completeness
records.witnessed recomputed tree heads covering the system’s records in the window that are graded witnessed/plural, of all such heads; records older than an hour with no covering head ⇒ not_met; plural and self-witnessed counts disclosed a witness-observed time; a same-operator witness is self-attested
records.chain_complete recomputed awaiting_human records closed by a human or expiry record, planned records confirmed, and contract promotions matched by epoch_boundary records chains the gateway never opened
records.integrity_verified recomputed audit row chains touching the system’s rows recompute intact (a signed checkpoint covers them, or every gap is a declared prune); forged/broken ⇒ not_met that a row was accurate when written
retention.floor recomputed effective retention ≥ floor_days, and no prune younger than the floor in the window; scope: documentation checks documentation artefacts record ids, statements and heads are permanent anyway; policies kept outside the platform
retention.declared_prunes recomputed every gap in the retained logs is a declared prune the pruned content
oversight.override_available recomputed run:abort held by someone, approval gates on state-changing grants, an oversight assignment declared that the assigned people are competent
oversight.exercised recomputed approval requests raised, decided by a human, and lapsed (requested n, decided m, lapsed k) the quality of the human decision
oversight.not_rubber_stamp indicator approval latency and denial-rate drift status that a decision was considered
authz.out_of_grant_refused recomputed out-of-grant actions were only ever denied; the contract hash on every record equals the contract in force at its time the grant itself is a declaration
authz.least_privilege_declared declared grants, capability filters and argument policies bound, sealed whether the grants are the least needed
authz.human_identity_verified recomputed approvals decided by authenticated principals (2FA where policy requires) who sat at the keyboard
access.sensitive_reads_recorded recomputed actions carrying tag params.tag (e.g. hipaa.phi) with a record, n of m untagged traffic is not in the denominator
monitoring.daily_health recomputed health / liveness / drift evaluated every day of the window what a human did with the findings
monitoring.suspend_available recomputed autonomy suspension and run-abort controls available; every use sealed whether they were used when they should have been
change.promotions_sealed recomputed every contract promotion has an epoch_boundary record naming its approver the quality of the specification or the replay suite
change.no_unapproved_widening recomputed the lifecycle gate’s no-unapproved-widening rule held over the window upstream changes (a vendor’s model) — shown as drift
notice.before_first_turn recomputed per conversation: the notice record precedes the first assistant-turn record in time and log order, n of m (transparency) conversations outside the Brutor portal
notice.text_declared declared notice text digest sealed; surface declared whether the text is understood
synthetic.marking_declared declared content-marking approach declared content-level marking is not provided
incidents.reported_within_deadline recomputed reported_at − became_aware_at ≤ the framework’s deadline rule, n of m deadlines are shown, not enforced; awareness time is operator-entered
incidents.register_exists recomputed an incident register is kept; open incidents past deadline = 0 incidents nobody recorded
evidence.pointer_on_file recomputed an unexpired evidence pointer of kind params.kind (risk_assessment, data_governance, impact_assessment, eval_report, red_team_report, dpia) the content of the document
dependencies.contributing_assessed recomputed every declared dependency of the system is assessed at (at least) the tier the system is evaluated at — a shared service by the registry at its effective tier, an external agent by its supplier (evidenced by supplier_information on the edge) — or carries a sealed separability rationale, n of m edges undeclared delegation (a composition finding, not an input); the external supplier’s assessment itself
dependencies.supplier_information_on_file recomputed every dependency supplied by another legal entity (every external agent; a shared service with supplier_legal_entity) has an unexpired supplier_information pointer attached to that edge, pinned by hash, n of m the terms of the agreement
docs.technical_documentation recomputed a documentation export exists for the active contract version, digest sealed the adequacy of the skeleton
docs.registration_recorded declared an external registration id is recorded the registry entry is not queried
profile.field_declared declared a profile field (params.path, e.g. frameworks.eu-ai-act.qms_reference) is declared organisational duties the gateway cannot observe
judge.<template> judged a tier-B template over sampled runs, with agreement — no_manipulation, notice_clear, per_instructions, approval_rationale stated with human agreement, never merged with recomputed rows

New frameworks compose these; a genuinely new obligation adds one resolver.

A system that contributes to another system’s decision is assessed with it (RFC 0022; the Commission’s draft high-risk classification guidelines, May 2026). Under a catalog that sets composite_classification — the EU AI Act does — every system is evaluated at its effective tier: the highest of its own declared tier and the tier of every system that declares a contributing dependency on it, carried along chains (A → B → C). The declared tier stays on the profile as the owner’s claim; applicability, statements and retention floors use the effective one, and the Obligations board, the matrix and the per-system obligations view show both (risk_tier_evaluated / risk_tier_declared with the systems that raised it). A catalog without the flag evaluates declared tiers only — the rule is registry data, not code, so a final text that differs changes a flag, not the platform.

A dependency declared not to contribute to the decision needs a separability rationale, and the rationale is sealed as an ai.brutor.declaration record (context state: separability, authority = the dependency edge) — the same sealing as every profile save. The record id and time are on the edge and shown on both systems; changing or withdrawing the claim clears them (a changed claim is sealed again). A claim the core could not seal is saved but unsealed, raises a capture_gap inbox item, and does not satisfy dependencies.contributing_assessed until a later save seals it.

Catalog versions carry effective ranges (effective_to is exclusive). A statement is evaluated against the catalog text in force on the action’s date, and reports and bundles record the catalog_version they were computed under. The EU AI Act has two: 2024-08 (until 27 July 2026) and 2026-09 (the Digital Omnibus dates). Obligations introduced by a later version (introduced_in) do not exist in an earlier replay; renamed keys resolve through aliases.

Retention floors are framework data. For each AI System:

effective retention = max( tenant policy, max over the system's active frameworks of floor )

A framework is active for a system when the tenant has it enabled and the system’s profile does not declare it out of scope; the floor is the catalog’s floor for the system’s classification — under a catalog with composite classification (the EU AI Act) the system’s effective tier, see composite systems — (EU AI Act high-risk: 183 days; SOC 2: 365 days for any system, the default attestation period). A registry framework without a catalog contributes its registry retention_floor_days. A policy of keep already satisfies any floor.

The retention sweeper runs per tenant, so the tenant-level policy is raised to the largest floor of any of its systems — keeping more than one system needs is the only safe direction. Body scrubbing is not a record floor: record bodies and the row hashes records cite are untouched by it. GET /v1/admin/compliance/retention shows the composition (policy, floor, effective, which frameworks raised it); retention.floor recomputes against the effective value and the declared prunes.