KB-3B2F
dot-iu-cutter v0.4 — Privilege Matrix Design
8 min read Revision 1
dot-iu-cutterv0.4credential-designprivilege-matrixdieu44
dot-iu-cutter v0.4 — Privilege Matrix Design
document_path: knowledge/dev/laws/dieu44-trien-khai/v0.4-credential-design/dot-iu-cutter-v0.4-privilege-matrix-design-2026-05-16.md
revision: r1
date_authored: 2026-05-17
cycle_date_label: 2026-05-16
author: Agent (Claude Code CLI, Opus 4.7 1M)
phase: v0.4 — credential-cycle DESIGN (privilege matrix)
status: design_only_pending_gpt_review
⛔ DESIGN ONLY. The matrix below is the TARGET grant contract. NO GRANT is issued. NO role exists. Applied only in a future GPT-gated cycle, and only after the isolated dry-run (doc 6) proves it.
§1 — Scope & Source of Truth
schema: cutter_governance (12 base tables, v0.2 structural inventory; 12
observe views + cutter_ro from v0.3 — NONE of which appear below: writers
get ZERO view / cutter_ro privilege).
table_set (v0.3 inventory §1): decision_backlog_entry, dot_pair_signature,
cut_change_set, cut_change_set_affected_row, verify_result,
canonical_address_alias, manifest_envelope, manifest_unit_block,
review_decision, decision_backlog_history, decision_backlog_dependency,
decision_backlog_sweep_log.
operations_observed (skeleton phases.py/ledger.py @689e53e): INSERT
(append), one state UPDATE (decision_backlog_entry.status via CAS), one
write-once lineage UPDATE (review_decision.superseded_by_review_decision_id),
SELECT (idempotency/guards/lineage). NO DELETE/TRUNCATE anywhere
(db_adapter raises AppendOnlyViolation).
§2 — Full Access Matrix (cutter_exec | cutter_verify)
legend: S=SELECT I=INSERT Uc=column-scoped UPDATE · = NO privilege
# table cutter_exec cutter_verify
1 decision_backlog_entry S I Uc(status) S I Uc(status)
2 dot_pair_signature S I S I
3 cut_change_set S I S I (CD-8/CD-10)
4 cut_change_set_affected_row I S I
5 verify_result · S I
6 canonical_address_alias · · (OD-2 defer)
7 manifest_envelope S I S
8 manifest_unit_block S I S
9 review_decision S I Uc(superseded_by_review_decision_id) S
10 decision_backlog_history S I S I
11 decision_backlog_dependency S I ·
12 decision_backlog_sweep_log I ·
schema cutter_governance USAGE USAGE (no CREATE)
cell_notes:
1 both: Uc(status) only; the legal transition set differs per principal
and is enforced in code (state_machine.compare_and_set / OD-SM-1), not
by the grant. exec: BIRTH→…→cut_applied + escalation-free path;
verify: cut_applied→verified_complete | verify_failed_escalated;
verify also INSERTs an escalation entry on failure.
3 exec INSERTs the cut; exec SELECT for G-CUT-ONCE. verify INSERTs only
the FORWARD compensating change set on failure + SELECTs the cut under
check. NEITHER gets UPDATE on cut_change_set → cut_change_set.
verifier_signature_id stays NULL forever (OD-6). CD-8/CD-10 open.
4 exec INSERT (cut affected rows; S optional, omitted = least priv);
verify S+I (compensating affected rows + read).
5 verify_result is verify-only: exec has NO privilege at all (strong SoD
— the executor cannot read or fabricate a verification record).
6 canonical_address_alias: ZERO privilege for both — alias fully deferred
(OD-2); the Stub canonicalization is alias-free; no row is ever written
in v0.4.
7,8 manifest pair: exec authors it during REVIEW; verify only reads it.
9 review_decision: exec authors + write-once supersede stamp; verify
reads the approved decision. verify gets NO write here.
11,12 dependency + sweep_log are exec-lane-only (sweep = OD-SM-3 same
agent; dependency declared at mark/review). verify has none.
§3 — The Append-Only Invariant & How It Is Preserved
layer_1 code: db_adapter.delete()/truncate() raise AppendOnlyViolation;
ledger writers are INSERT + transition_status (validate→CAS→append
history) + write-once supersede stamp. No code path issues a general
UPDATE or any DELETE.
layer_2 grant (recommended, CD-1): the ONLY UPDATE privileges granted are
COLUMN-SCOPED:
GRANT UPDATE (status) ON cutter_governance.decision_backlog_entry
GRANT UPDATE (superseded_by_review_decision_id)
ON cutter_governance.review_decision
Every other column of every table is physically non-UPDATE-able by the
writers, and NO DELETE/TRUNCATE privilege is ever granted. The DB denies
a mutation the code forgot to.
layer_3 catalog (verification, not enforcement): post-grant introspection
(information_schema.role_table_grants / column_privileges) must show
EXACTLY this matrix — no UPDATE outside the 2 columns, 0 DELETE, 0
TRUNCATE, 0 REFERENCES, 0 TRIGGER, 0 grant on public/views/cutter_ro.
not_used (consistent with BATCH-1, v0.2 inventory §5): NO CHECK, NO
trigger, NO DEFAULT, NO PG enum, NO RLS is added — the column-scoped
GRANT is an ACCESS control, not a data constraint; business invariants
remain app/agent-enforced.
write_once (superseded_by): code stamps NULL→id once
(ledger.supersede_review_decision); the column-scoped UPDATE grant
permits the column but cannot itself forbid a second write — single-write
discipline stays a code invariant (test_rereview_chains_prior_and_
supersedes proves it). Documented as accepted (CD-1 note).
§4 — Global Denials (both principals, all objects)
NO DELETE / NO TRUNCATE / NO REFERENCES / NO TRIGGER on any table.
NO DDL (CREATE/ALTER/DROP) on any object.
NO GRANT / NO REVOKE / NO role administration.
NO object ownership; NO ALTER DEFAULT PRIVILEGES.
NO SUPERUSER / NO CREATEDB / NO CREATEROLE / NO REPLICATION / NO BYPASSRLS.
NO membership in workflow_admin / directus / postgres / cutter_ro / any role.
NO privilege on schema public or any object outside cutter_governance.
NO privilege on the 12 v_*_observe views (those belong to cutter_ro only;
v0.3 left them at 13 grants to cutter_ro — UNCHANGED).
NO base-table grant added to cutter_ro (cutter_ro stays views-only).
ALTER cutter_ro / GRANT cutter_ro TO … : OUT OF SCOPE (B-4 stays deferred).
§5 — UPDATE Posture Decision (CD-1, expanded)
option_A INSERT-only, NO UPDATE at all. status & supersede would then need
to be modeled as append-only rows (no in-place state). REJECTED: the
schema has decision_backlog_entry.status and review_decision.
superseded_by_review_decision_id as in-place columns (v0.2 inventory);
changing that = DDL, out of scope; the code already does CAS UPDATE.
option_B INSERT + TABLE-level UPDATE on the 2 tables. REJECTED: lets a
compromised/buggy writer rewrite ANY column (payload, signatures, ids).
option_C INSERT + COLUMN-scoped UPDATE on exactly status /
superseded_by_review_decision_id. RECOMMENDED — minimal, DB-enforced,
no trigger/CHECK, exactly matches the only two UPDATEs the code issues.
recommendation: C.
§6 — Open Decisions (consolidated; for GPT)
CD-1 UPDATE posture → RECOMMEND C (column-scoped UPDATE grants).
CD-2 both principals get Uc(status); legality split enforced in code.
CD-3 SECURITY DEFINER functions vs direct grants → see secret-custody doc;
RECOMMEND direct grants for v0.4 (auditable, no owner-priv escalation).
CD-7 decision_backlog_sweep_log SELECT for exec → RECOMMEND write-only.
CD-8 cutter_verify INSERT on cut_change_set vs route-back-to-exec.
CD-9 CONNECTION LIMIT value per writer role.
CD-10 signer lane for the compensating change set (DOT-992 vs DOT-991).
CD-11 dot_pair_signature single shared table — lane separation stays
code/crypto only (no DB row-level split without DDL). Accept?
End of privilege matrix design (design only; no GRANT issued).