Skip to main content
Version: v5.6.1

Governance foundation

Included from Basic tierpacks:security-governance

The foundation primitives: the shared governance objects every governance pack consumes — built once as base-platform machinery, so no pack re-invents them. Packs gate surfaces and content over these objects, never the primitives themselves. The API surface is GovernanceService (see opschain docs api-v2); every event the machinery emits is catalogued on opschain docs events; the per-RPC determination × pack × step-up table is opschain docs governance-matrix. Hands-on guides: opschain learn governance-controls-evidence, governance-campaigns and governance-waivers.

Controls and the catalog​

A control is a named, owned, reviewable statement about enforcement — not an enforcement instrument itself. Controls wrap existing mechanisms: instrument_refs points at the rows that implement the control by {kind, id} — an authorisation policy, a notification mandate, a maintenance posture, a trust grant — leaving every instrument's schema untouched, and the authz decide path never consults the catalog. A control with zero instrument refs is legal: a procedural control attested by humans (e.g. "licence keys are custodied offline"), which is what makes the catalog a complete compliance surface rather than a code mirror.

Attributes: key (unique, immutable), name, domain (security | change | agent, immutable), description, instrument_refs, owner_role (the role accountable for reviews) and status (active | retired — retired controls stay in the catalog because attestations cite them).

The evidence ledger and attestations​

An attestation is the dated statement that a named control was reviewed/verified: control, attestor, authn strength, verdict (verified | verified_with_exceptions | failed), notes and evidence_refs — typed citations, never copies: an artifact by sha256: blob digest, an audit-event id, a campaign id, an export manifest hash, a waiver id.

Attestations land in the evidence ledger: a separate append-only store (the events spine is disqualified — its rows are updated in place by live write paths) where every row carries prev_hash/row_hash, a SHA-256 chain from genesis to head. The database enforces the posture: a guard trigger refuses rewriting any hashed content after insert. VerifyEvidenceLedger recomputes the whole chain and names the first broken row.

Legal hold: a flagged ledger row is exempt from every purge/retention path until explicitly unflagged. The guard triggers refuse every content UPDATE and TRUNCATE, and permit a DELETE only for rows a verified sealed archive segment covers (see sealed-segment archival below) — a held row refuses even then, naming the exemption, and the archival sweep never seals past a held row. Flagging and unflagging are audited administrative acts on the step-up floor. The flag is administrative metadata outside the hashed content, so holds never break the chain.

A held entry's typed citations extend the hold to the evidence it cites : transcript citations pin transcript exchanges against the retention sweep, and run citations pin a run's log evidence — its sealed log_segments against the retention sweep and its log_lines owners against the migrate-logs purge — until the hold is unflagged. Each exemption set is re-read every batch, so a hold placed mid-sweep still bites.

Gating: ledger recording is base (campaign closure appends its attestation unentitled); reading the ledger as the auditor, verifying, writing attestations and administering legal holds are packs:security-governance surfaces. Reading and verifying ride the /governance/evidence readable determination and attestation appends its updatable twin (the user plane); legal hold stays an admin-plane operational act. Attestation writes and legal-hold changes require a stepped-up session (47).

Sealed-segment archival — the ledger's retention​

The ledger can never ride row purges — deleting rows breaks the chain — so its retention is a chain operation, run by the retention sweep on the retention.evidence_ledger_days hot window (default 730 days; the mechanics, boundary rules and failure modes are on opschain docs retention): the aged contiguous prefix is verified, exported to the blobstore as a chain-preserving segment, read back and re-verified, sealed inside the chain by a segment_seal ledger row carrying the segment's range, closing hash, row count and blob digest, and only then deleted — evidence before purge, enforced by the database (a delete without a covering verified segment refuses). Verification seeds at the sealed boundary's closing hash, so the chain verifies across archived segments, and the verify verdict reports the boundary (archived_through_seq) and the segment count. A legal-held row caps the segment, an unverifiable prefix refuses archival with the loud warn:governance:ledger_archive_blocked event, and archived segments are kept to the operator's regulatory horizon — the sweep never deletes them.

The export lane — pull, hash-chained​

ExportEvidenceLedger (readable on /governance/evidence + the pack) is the auditor-facing pull lane: one batch of rows after a after_seq cursor, in chain order, every row carrying its stored prev_hash/row_hash verbatim, with the batch's SHA-256 over the canonical NDJSON encoding (opschain-evidence-export/v1). Batches chain — the next batch's first row links to the previous batch's closing hash — so the accumulated export re-verifies offline without the server. The CLI verb is opschain governance evidence export (opschain docs cli/governance-evidence-export), which re-encodes the served rows and refuses on a digest mismatch before writing anything. Pushing to a SIEM is a separate transport on its own trigger; this lane is the archival-integrity half.

The posture report​

GetGovernancePostureReport (readable on /governance/evidence + the pack; CLI opschain governance posture) renders the governance posture in the shared audit-report grammar — findings, coverage, framework mapping, honest posture statement — over the control catalog, the ledger's hot window and the exceptions register: per-control findings (control_attestation_failed high, control_never_attested and control_review_overdue medium, control_attested_with_exceptions low, waiver_active info — an excused control is posture an auditor reads, never a silent pass), per-domain coverage (the mapping onto the security/change/agent domains), the continuous-control-monitoring metric set (control coverage, attestation verdicts, campaign progress and overdue reviews, waiver expiries, legal-hold counts, ledger size and archived boundary) and the chain-verify verdict. Every report states its honest posture: a tool-assisted attestation of recorded posture, never a certification.

Review campaigns and cadences​

A campaign turns a point-in-time dataset into a governed sign-off: items (subject_kind/subject_id + reviewer) each carry a decision — retain | revoke | flag — recorded in the shared approval grammar (the step-gate audit:…:approve|reject event family, never a new one). A revoke decision does not auto-revoke in v1: it opens a named work item for the operator. Closing a fully-decided campaign appends the closing attestation citing the campaign: all-retain closes verified, any revoke or flag closes verified_with_exceptions. Campaign decisions and closure are step-up gated, and both are reviewer verbs on the user plane: updatable on /governance/campaigns — a reviewer is an ordinary user with a grant, never a server admin. The campaign reads ride the same family's readable determination, and ListReviewCampaigns pages newest first with an open/closed status filter.

A cadence is "this control is reviewed every N": a 5-field cron schedule riding the existing scheduler leader tick. A due cadence opens a campaign of its campaign_kind, due before the next cron occurrence, and advances next_due_at; concurrent engines claim disjoint due sets, so a firing happens exactly once, and while a cadence's previous campaign is still open the schedule advances without opening a duplicate. An open campaign past due_at plus the cadence's grace_seconds draws the loud warn:review_cadences:lapsed event once — overdue reviews are themselves compliance evidence. A campaign with no items cannot close: nothing was reviewed, so nothing can honestly be attested (the pack kinds populate items; the first is the access review below).

The access-review campaign​

The access_review campaign kind is the security pack's first built-in campaign content — the foundation's proving consumer. Its items are sourced server-side from the live access-review dataset (the same data opschain admin authz review-export exports, which stays base): every grant at the per-identity (rule × principal) grain — a user grant keys on its user_id, never the ambiguous username — every superuser/block system assignment, and the api-token and runner-registration-token inventories. Each item carries a subject_summary: the point-in-time human statement the reviewer decides on, with every identity badged by its provider, so the campaign reviews a snapshot that cannot drift with the live rows.

Two ways in, one object: OpenAccessReviewCampaign (admin + the packs:security-governance entitlement — the campaign kind is pack content behind the one securityGovernanceGate, never a parallel gate) opens one on demand with an optional reviewer and due date; a cadence whose campaign_kind is access_review sources the same items when it fires on the leader tick, in the firing's transaction, so a sourcing failure rolls the firing back and retries next tick. Decisions, closure and the closing attestation are the foundation machinery unchanged.

The secret-rotation campaign​

The secret_rotation campaign kind is the rotation-cadence content: "secrets under this vault are rotated every N, attested", on the same foundation objects. A cadence whose campaign_kind is secret_rotation fires a campaign whose items are the vault's secret paths at firing time — one secret_path item per node, the same value-free enumeration posture as opschain secret audit: paths are listed, values are never read or recorded anywhere. Reviewers retain (rotated) or flag each path and close the campaign into the ordinary attestation. Sourcing needs a listing-capable vault backend (the file store and the KV backends both enumerate); a vault that cannot enumerate rolls the firing back loudly and retries, never opening an unclosable empty campaign.

Work items — revoke opens work​

A revoke decision does not auto-revoke in v1 — in the decision's own transaction it opens a named work item (Revoke <subject_kind> <subject_id>, carrying the subject's snapshot summary), one per campaign item however often the decision is re-recorded. The operator remediates by hand and records it with CompleteGovernanceWorkItem (updatable on /governance/work_items + step-up governance.work_item_complete — remediation is operator-user work; audited as api:governance_work_items:complete); a completed item refuses a second completion rather than overwriting the recorded notes. ListGovernanceWorkItems lists them newest first, filterable by open/done, behind the same family's readable determination. The work-item surfaces stay base (no pack) so an entitlement lapse never hides open remediation work; auto-remediation is deliberately later pack content.

The waiver register​

A waiver is the exception shape, built once: time-boxed (mandatory future expires_at; renewal is review-before-renewal, a new waiver), never self-granted (the recording caller is the approver and must differ from requested_by — refused at the API and by a schema constraint), reasoned (justification plus a typed external reference) and scoped to a control plus an estate path. ListWaivers is the register, soonest expiry first (readable on /governance/waivers); granting stays an admin-plane act. Waivers are distinct from break-glass: a waiver excuses a control for a scope and a period; break-glass is an authentication posture.

Pack entitlement strings​

The canonical pack vocabulary is registered in pkg/licence: packs:agents, packs:security-governance, packs:change-governance, packs:agent-governance, packs:ai-agents, packs:mintpress, plus the separately-licensed families packs:ai-agents-<domain> (hard dependency on packs:ai-agents) and packs:bindings-<framework>. All are pack-shaped entitlements enforced through the one gate idiom (CheckFeature/CheckPackUse, the typed PACK_ENTITLEMENT_REQUIRED refusal naming the pack, funnel posture — controls ungated, admin/attest surfaces gated, polite non-destructive lapse). Registration fixes the spellings; the binding packs themselves are future, demo-feedback-gated content.

Plane placement​

Governance is a feature domain whose users are security officers, risk owners, reviewers and auditors — not server administrators. The reviewer/auditor-facing verbs live on the user plane behind the delegable /governance/<surface> rule-path family (controls, campaigns, evidence, waivers, work_items — the metadata-writer pattern): a path-RBAC rule granting readable there delegates the reads, updatable the reviewer writes, and admin status never implies either. The operational verbs — catalog curation, cadence configuration, hand-opening campaigns, legal hold, waiver grants — stay behind the /admin updatable determination. In the GUI the domain lives under /governance, never /admin. The CLI mirrors the same split: the feature verbs ride a normal login under the top-level opschain governance noun (controls, campaigns, evidence, waivers, work-items, owners), the operational subset under opschain admin governance (cadences, controls curation, evidence hold/unhold, waivers create, campaigns open), and each verb's manual lives at opschain docs cli/<command-path>.

What changes governance behaviour​

  • GovernanceService reviewer verbs (governance rule-path family + step-up + pack, per surface) and operational verbs (admin + step-up + pack, per surface) — see opschain docs api-v2.
  • The scheduler leader tick drives cadence firing and lapse detection; a halted or quiesced system pauses the sweeps with the rest of the tick.
  • The packs:security-governance entitlement gates the evidence-ledger read/verify/export/attest/legal-hold surfaces and the posture report; a lapsed entitlement freezes that administration without touching recording or enforcement.
  • retention.evidence_ledger_days (config.yaml) sets the ledger's hot window; the retention sweep runs the sealed-segment archival on the scheduler leader (opschain docs retention).