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).

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.4-credential-design/dot-iu-cutter-v0.4-privilege-matrix-design-2026-05-16.md