KB-3252
dot-iu-cutter v0.4 — Credential Dry-Run Verification Plan
15 min read Revision 1
dot-iu-cutterv0.4credential-authoringverification-plandieu44
dot-iu-cutter v0.4 — Credential Dry-Run Verification Plan
document_path: knowledge/dev/laws/dieu44-trien-khai/v0.4-credential-authoring/dot-iu-cutter-v0.4-credential-dry-run-verification-plan-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 DRY-RUN AUTHORING (verification plan)
status: authoring_only_not_executed_pending_gpt_review
⛔ AUTHORING ONLY. No query here is run. This is the harness the SEPARATE, GPT-gated dry-run cycle will run inside the isolated env only. Every check is structural / catalog-set based — NOT a fragile rendered-string comparison. (Carries the feedback memory lesson:
pg_get_constraintdefschema-qualifier false-negative forced a needless prod rollback in P0-6/P0-5. We compare privilege SETS viaaclexplode, never bare strings.)
§0 — Verification Doctrine
method: build the ACTUAL (grantee, object, privilege[, column]) tuple set
from system catalogs via aclexplode(); build the EXPECTED set from the
matrix; assert symmetric difference == ∅. PASS iff EVERY expected tuple is
present AND no unexpected tuple exists.
never: parse pg_get_*def() text, never substring-match a rendered ACL, never
compare a psql \dp dump as a string. Structural sets only.
PASS_iff: all V-* PASS, all allow-probes succeed, all deny-probes are
REFUSED by PG, baseline/rollback checks clean, prod provably untouched.
any_fail: => matrix/role spec revised => re-design => GPT re-review. NO
production credential until clean PASS + GPT PASS.
§1 — Expected Privilege Set (the contract being asserted)
schema_usage:
cutter_governance -> {cutter_exec: USAGE, cutter_verify: USAGE}
cutter_governance CREATE -> {} (neither principal)
cutter_exec table tuples:
decision_backlog_entry : SELECT, INSERT ; UPDATE col=status
decision_backlog_history : SELECT, INSERT
decision_backlog_dependency : SELECT, INSERT
decision_backlog_sweep_log : INSERT (NO SELECT)
manifest_envelope : SELECT, INSERT
manifest_unit_block : SELECT, INSERT
review_decision : SELECT, INSERT ; UPDATE col=superseded_by_review_decision_id
cut_change_set : SELECT, INSERT
cut_change_set_affected_row : INSERT (NO SELECT)
dot_pair_signature : SELECT, INSERT
verify_result : {} (none)
canonical_address_alias : {} (none)
cutter_verify table tuples:
decision_backlog_entry : SELECT, INSERT ; UPDATE col=status
decision_backlog_history : SELECT, INSERT
dot_pair_signature : SELECT, INSERT
cut_change_set : SELECT, INSERT
cut_change_set_affected_row : SELECT, INSERT
verify_result : SELECT, INSERT
manifest_envelope : SELECT
manifest_unit_block : SELECT
review_decision : SELECT
decision_backlog_dependency : {} (none)
decision_backlog_sweep_log : {} (none)
canonical_address_alias : {} (none)
global_denied (both, all objects): DELETE, TRUNCATE, REFERENCES, TRIGGER,
any GRANT OPTION, any UPDATE column other than the 2 above.
total_expected_update_columns: exactly 2 (entry.status, review_decision.superseded_by_review_decision_id) — one per principal-eligible table, granted to BOTH principals only where matrix says so.
§2 — Catalog Checks (structural; set-equality)
V-01 role_attributes:
source: pg_roles WHERE rolname IN ('cutter_exec','cutter_verify')
assert per role: rolcanlogin=t, rolsuper=f, rolcreatedb=f,
rolcreaterole=f, rolreplication=f, rolbypassrls=f, rolinherit (record),
rolconnlimit=2.
PASS iff both rows exactly match; 0 extra cutter_* roles created.
V-02 connection_limit:
pg_roles.rolconnlimit = 2 for BOTH (explicit, not -1/0). FAIL on any other value.
V-03 no_membership:
pg_auth_members: 0 rows where member = cutter_exec/cutter_verify OR
roleid = cutter_exec/cutter_verify (they neither inherit nor are inherited).
Also assert neither is a member of cutter_ro / workflow_admin / directus /
postgres / pg_* default roles.
V-04 no_object_ownership:
scan pg_class.relowner, pg_namespace.nspowner, pg_proc.proowner,
pg_type.typowner, pg_authid via pg_shdepend WHERE refobjid = roleoid
AND deptype='o'. Expected count = 0 for both principals.
V-05 schema_usage_exact:
has_schema_privilege('cutter_exec','cutter_governance','USAGE')=t,
has_schema_privilege('cutter_verify',...,'USAGE')=t,
has_schema_privilege(...,'CREATE')=f for both.
Cross-check via aclexplode(nspacl) of cutter_governance: privilege set for
each principal == {USAGE} exactly.
V-06 table_privileges_set_equality (THE core structural check):
WITH actual AS (
SELECT r.rolname AS grantee, c.relname AS tbl, a.privilege_type
FROM pg_class c
JOIN pg_namespace n ON n.oid=c.relnamespace AND n.nspname='cutter_governance'
CROSS JOIN LATERAL aclexplode(c.relacl) a
JOIN pg_roles r ON r.oid=a.grantee
WHERE r.rolname IN ('cutter_exec','cutter_verify')
AND c.relkind='r')
Compare `actual` to the §1 expected table tuple set. Assert:
(expected EXCEPT actual) = ∅ AND (actual EXCEPT expected) = ∅.
Also assert a.is_grantable = false for EVERY actual row (no GRANT OPTION).
NOTE: table-level set must NOT contain UPDATE for entry/review_decision
(those are column-scoped — verified in V-07), and must NOT contain
SELECT for decision_backlog_sweep_log (exec) or cut_change_set_affected_row (exec).
V-07 column_privileges_set_equality:
WITH actual_col AS (
SELECT r.rolname grantee, c.relname tbl, att.attname col, ac.privilege_type
FROM pg_class c
JOIN pg_namespace n ON n.oid=c.relnamespace AND n.nspname='cutter_governance'
JOIN pg_attribute att ON att.attrelid=c.oid AND att.attnum>0 AND NOT att.attisdropped
CROSS JOIN LATERAL aclexplode(att.attacl) ac
JOIN pg_roles r ON r.oid=ac.grantee
WHERE r.rolname IN ('cutter_exec','cutter_verify'))
Assert actual_col == EXACTLY:
{(cutter_exec, decision_backlog_entry, status, UPDATE),
(cutter_verify, decision_backlog_entry, status, UPDATE),
(cutter_exec, review_decision, superseded_by_review_decision_id, UPDATE)}
and NOTHING else. is_grantable=false on all. (Symmetric-difference = ∅.)
V-08 no_extra_update:
derived from V-06+V-07: total distinct (grantee,tbl,col) UPDATE tuples = 3
exactly (2 on entry — both principals, 1 on review_decision — exec only);
0 table-level UPDATE; 0 UPDATE on any other column of any table.
V-09 no_destructive_privs:
from V-06 actual set: 0 rows with privilege_type IN
('DELETE','TRUNCATE','REFERENCES','TRIGGER') for either principal on ANY
cutter_governance object (tables AND views).
V-10 no_observe_view_grants:
aclexplode over relkind='v' (the 12 v_*_observe) in cutter_governance:
0 rows where grantee IN (cutter_exec,cutter_verify). Views remain
cutter_ro-only.
V-11 no_canonical_address_alias_grant:
aclexplode(relacl) of cutter_governance.canonical_address_alias AND its
attacl: 0 tuples for cutter_exec OR cutter_verify (table and column level).
V-12 no_out_of_schema_priv:
scan pg_class/pg_namespace for any object NOT in cutter_governance
(esp. schema public): 0 relacl/attacl tuple grants to either principal.
has_schema_privilege(principal,'public','CREATE')=f. (public USAGE is the
PG cluster default, note N-1, NOT introduced here — record as NOTE, not FAIL.)
V-13 cutter_ro_unchanged (byte-state):
pg_roles row for cutter_ro identical to v0.3 inventory §4 (NOLOGIN,
NOSUPERUSER/NOCREATE*/NOBYPASSRLS, 0 membership);
aclexplode set for cutter_ro == exactly {schema cutter_governance USAGE}
∪ {SELECT on each of the 12 v_*_observe} = 13 grant tuples, 0 base-table
grant, 0 write privilege. Symmetric difference vs the v0.3 expected set = ∅.
V-14 directus_unchanged:
counts: directus_collections=164, directus_permissions=1173,
directus_roles=9, directus_policies=8, directus_access=9 (== v0.3 §5
baseline). cutter_governance still UNregistered in Directus.
V-15 rls_unchanged:
SELECT count(*) FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
WHERE n.nspname='cutter_governance' AND c.relrowsecurity; expected 0.
0 pg_policy rows on any cutter_governance relation.
V-16 schema_creation_denied:
has_schema_privilege(principal,'cutter_governance','CREATE')=f for both
(re-assert; ensures USAGE without CREATE).
V-17 password_encryption_scram (CD-5):
pg_authid.rolpassword for both begins with 'SCRAM-SHA-256$' (structural
prefix test, not value disclosure — never SELECT/print the hash body).
§3 — Behavioural Allow-Probes (must SUCCEED; rolled back in-txn)
note: each probe runs as the named principal in the isolated env, inside a
SAVEPOINT, asserting success, then ROLLBACK TO SAVEPOINT so no row persists
(empty-schema fidelity preserved). FK-satisfying minimal parent rows are
staged within the same outer txn as needed.
A-EXEC:
AE-1 INSERT decision_backlog_entry -> success
AE-2 UPDATE decision_backlog_entry SET status=... (granted col) -> success
AE-3 SELECT decision_backlog_entry -> success
AE-4 INSERT decision_backlog_history / _dependency -> success
AE-5 INSERT decision_backlog_sweep_log -> success
AE-6 INSERT manifest_envelope / manifest_unit_block ; SELECT both -> success
AE-7 INSERT review_decision ; UPDATE review_decision SET
superseded_by_review_decision_id=... -> success
AE-8 INSERT cut_change_set ; SELECT cut_change_set -> success
AE-9 INSERT cut_change_set_affected_row -> success
AE-10 INSERT/SELECT dot_pair_signature -> success
A-VERIFY:
AV-1 INSERT/SELECT verify_result -> success
AV-2 INSERT/SELECT dot_pair_signature -> success
AV-3 INSERT cut_change_set (fwd compensation, CD-8/10) -> success
AV-4 INSERT/SELECT cut_change_set_affected_row -> success
AV-5 INSERT decision_backlog_entry (escalation) ; UPDATE status ;
INSERT decision_backlog_history -> success
AV-6 SELECT manifest_envelope / manifest_unit_block / review_decision -> success
§4 — Behavioural Deny-Probes (must be REFUSED by PG: SQLSTATE 42501)
D-1 ANY DELETE on ANY cutter_governance table (both principals) -> denied
D-2 ANY TRUNCATE on ANY table (both) -> denied
D-3 UPDATE decision_backlog_entry SET payload=... (non-granted col, exec & verify) -> denied
D-4 UPDATE decision_backlog_entry SET entry_id=... (PK / non-granted) -> denied
D-5 UPDATE review_decision SET <any col != superseded_by_review_decision_id> (exec) -> denied
D-6 UPDATE review_decision (any) by cutter_verify -> denied (verify read-only here)
D-7 ANY UPDATE on cut_change_set (either principal; OD-6 keeps
verifier_signature_id NULL forever) -> denied
D-8 cutter_exec any verb on verify_result -> denied (strong SoD)
D-9 cutter_exec SELECT decision_backlog_sweep_log -> denied (CD-7 write-only)
D-10 cutter_exec SELECT cut_change_set_affected_row -> denied (INSERT-only)
D-11 cutter_verify INSERT/UPDATE manifest_envelope/manifest_unit_block/review_decision -> denied
D-12 cutter_verify any verb on decision_backlog_dependency / decision_backlog_sweep_log -> denied
D-13 EITHER principal any verb on canonical_address_alias -> denied
D-14 EITHER principal SELECT/INSERT any v_*_observe view -> denied
D-15 EITHER principal any access to a schema public object / any
non-cutter_governance object -> denied
D-16 EITHER principal CREATE TABLE in cutter_governance / any DDL -> denied
D-17 EITHER principal GRANT/REVOKE / ALTER ROLE / CREATE ROLE -> denied
D-18 EITHER principal REFERENCES or CREATE TRIGGER on any table -> denied
D-19 connect as cutter_ro, attempt any write on view/base table -> denied (cutter_ro unchanged, SELECT-on-views-only)
D-20 open a 3rd concurrent session per writer (CONNECTION LIMIT 2) -> refused (too many connections for role)
§5 — Rollback Verification (after the companion rollback draft runs)
RBV-1 pg_roles: 0 rows for cutter_exec / cutter_verify (both DROPped).
RBV-2 aclexplode over all cutter_governance objects: 0 tuple for either
principal (REVOKE was exact inverse; no residue).
RBV-3 cutter_ro byte-state == V-13 expected (UNCHANGED by rollback).
RBV-4 12 base tables / 12 views intact, 0 rows, schema present.
RBV-5 RLS still 0; Directus counts still 164/1173/9/8/9.
RBV-6 NO CASCADE side effect: object/owner counts identical to pre-dry-run.
§6 — Prod-Untouched Gate
PU-1 dry-run cluster system identifier != prod 7611578671664259111
(asserted before AND after; recorded both times).
PU-2 prod reached read-only ONLY for the pg_dump source (command plan);
never connected by §3 SQL or any probe.
PU-3 every catalog/probe query confined to the ephemeral container; the
env is torn down after RBV; only KB result docs persist.
§7 — Harness Discipline (false-negative guard)
- all privilege assertions are SET operations on aclexplode() output, not
string comparisons of \dp / pg_get_*def() (feedback memory: schema-
qualifier mismatch caused a false-negative + needless prod rollback in
dot-iu-cutter P0-6/P0-5 2026-05-16).
- expected sets are declared as explicit (grantee,object[,column],priv)
literals in §1; the harness computes symmetric difference and FAILS only
on a genuine structural delta.
- a deny-probe is PASS iff PG raises insufficient_privilege (SQLSTATE
42501) — not iff a generic error occurs (distinguish 42501 from 42P01
etc. so a missing object is not mistaken for a denial).
- on CONNECTION LIMIT probe, PASS iff PG refuses the 3rd login with the
too-many-connections-for-role error specifically.
End of credential dry-run verification plan (authoring only; nothing executed).