KB-789A
dot-iu-cutter v0.4 — Credential & Principal Strategy Design (design only; no credential creation) (2026-05-16)
8 min read Revision 1
dot-iu-cutterdieu44v0.4tier2credentialprincipalleast-privilegedesign-only
dot-iu-cutter v0.4 — Credential & Principal Strategy Design
document_path: knowledge/dev/laws/dieu44-trien-khai/v0.4-design/dot-iu-cutter-v0.4-credential-and-principal-strategy-design-2026-05-16.md
revision: r1
date: 2026-05-16
author: Agent (Claude Code CLI, Opus 4.7 1M)
phase: v0.4 — Tier 2 credential/principal DESIGN (companion to design-master)
status: design_only_pending_gpt_review
⛔ DESIGN ONLY. NO credential is created. NO role is created. NO GRANT is issued. NO password is minted. NO login/member binding. Every principal named here is a target for a SEPARATE, explicitly-authorized credential cycle that runs only after this design and the flow design pass GPT review.
§1 — Non-Negotiable: cutter_ro Is Read-Only
C-1 cutter_ro is NOLOGIN, 0 memberships, SELECT-only on 12 observe views +
schema USAGE. It has ZERO write privilege and MUST NOT appear on any
write path, ever.
C-2 the cutter-agent write path MUST NOT authenticate as cutter_ro, MUST
NOT be granted cutter_ro for writing, MUST NOT extend cutter_ro.
C-3 cutter_ro stays exactly as v0.3 left it. v0.4 design proposes NO change
to cutter_ro, its grants, its views, or its (deferred) login binding.
C-4 read needs of the write path (idempotency lookups, guard checks) are
served by the WRITER principal's own SELECT on base tables (it needs
base-table read to enforce invariants), NOT by cutter_ro and NOT by the
observe views (which are redacted and intended for external observers).
§2 — Proposed Writer Principals (DESIGN; not created)
recommended split = TWO writer principals (OD-3; alternative 1 or 3 below):
cutter_exec (LOGIN, least-privilege):
- drives MARK, REVIEW-persistence, CUT
- INSERT on the MARK/REVIEW/CUT family tables + executor signature
- UPDATE(status) on decision_backlog_entry
- identity behind the DOT-991 executor signature lane
cutter_verify (LOGIN, least-privilege):
- drives VERIFY independently
- INSERT on verify_result, verifier dot_pair_signature, compensating
cut_change_set(+affected_row), escalation decision_backlog_entry,
decision_backlog_history; UPDATE(status) on decision_backlog_entry
- identity behind the DOT-992 verifier signature lane
rationale: VERIFY's value is INDEPENDENCE. If the same DB principal both
cuts and verifies, the verify attestation is not an independent control.
Two principals = real separation of duty, mirrored at the DB layer.
alternatives for GPT (OD-3):
A) 1 writer principal (cutter_rw): simpler secret custody, WEAKER SoD —
SoD then rests only on code paths + signature keys, not DB identity.
C) 3 principals (cutter_mark / cutter_cut / cutter_verify): maximal
isolation, more secrets to custody; likely over-engineered for v0.4.
recommendation: B (two principals) — best SoD/complexity trade-off.
§3 — Least Privilege (target grant model — applied LATER, not here)
P-1 grant ONLY INSERT (+ table-level UPDATE, with status-only enforced in
code/invariant per BATCH-1) on exactly the tables each principal writes
(see write-path doc §7). No SELECT beyond what invariant checks need.
P-2 NO DELETE, NO TRUNCATE, NO REFERENCES, NO TRIGGER, NO DDL, NO GRANT to
any cutter writer principal.
P-3 NO membership in workflow_admin / postgres / directus app role. The
writers are NOT superusers and CANNOT bypass RLS (none exists anyway).
P-4 the writers get NO privilege on schema `public` or any non-
cutter_governance object (scope strictly the governance schema).
P-5 NO ALTER DEFAULT PRIVILEGES — grants enumerated exactly, no auto-grant
of future objects (same discipline v0.3 used for cutter_ro).
P-6 reuse of the existing `directus` app role or `workflow_admin` as the
writer is FORBIDDEN (privilege blast radius + audit ambiguity).
§4 — Secret Custody (strategy only — no secret minted)
SC-1 each writer principal gets its OWN distinct PG password secret —
never shared, never the directus/workflow_admin password.
SC-2 custody pattern follows the established project convention: secrets
live in the VPS-side env file (the /opt/incomex/docker/.env pattern
already used for Agent Data / Directus creds), NOT in any repo, NOT in
Agent Data, NOT in this document.
SC-3 cutter_exec secret and cutter_verify secret are stored as SEPARATE
keys so the executor process cannot read the verifier's secret (and
vice-versa) — process-level isolation backs the DB-level SoD.
SC-4 rotation: design assumes rotatable passwords (ALTER ROLE … PASSWORD in
a future authorized op); no rotation schedule is set here.
SC-5 the agent NEVER prints, logs, or echoes a writer secret; harness/log
redaction is a code-cycle requirement, recorded here as a constraint.
SC-6 minting these secrets is OUT OF SCOPE — it happens in the dedicated
credential cycle, gated by GPT review of this design + sovereign prompt.
§5 — Executor / Verifier Separation & DOT-Pair Alignment
D-1 DOT-991 = executor lane: cutter_exec produces the executor
dot_pair_signature on every cut_change_set. DOT-992 = verifier lane:
cutter_verify produces the verifier dot_pair_signature on every
verify_result. This mirrors the project's established paired pattern
(DOT-…-EXECUTOR ↔ DOT-…-EXECUTOR-VERIFY) so cutter CUT/VERIFY aligns
with the existing DOT governance idiom rather than inventing a new one.
D-2 the two DOT lanes use DISTINCT signing identities/keys (signing-scheme
runtime is itself deferred — doc 6); v0.4 only fixes that the executor
signature and verifier signature MUST be independently keyed and that
dot_pair_signature.prior_signature_id chains each lane separately.
D-3 a verify_result whose verifier_signature shares identity with its
checked executor_signature is INVALID by design (G-VERIFY-SOD at the
crypto layer, not just the DB-principal layer).
D-4 mapping to schema: cut_change_set.executor_signature_id and
verify_result.executor_signature_id reference the DOT-991 lane;
verify_result.verifier_signature_id references the DOT-992 lane;
cut_change_set.verifier_signature_id handling = OD-6 (recommend leave
NULL; verifier signature recorded on verify_result only).
§6 — Interaction with Deferred B-4 (login/member binding)
B4-1 v0.3 deliberately left cutter_ro NOLOGIN with 0 memberships (B-4
deferred). v0.4 design does NOT bind cutter_ro and does NOT create the
writer login roles — it only specifies what they must be.
B4-2 when the credential cycle runs, the read-side B-4 (give cutter_ro a
consumer) and the write-side (create cutter_exec/cutter_verify) are
SEPARATE authorized steps; this design must not let one imply the other.
B4-3 no member/group binding, no GRANT cutter_ro TO …, no ALTER cutter_ro
WITH LOGIN — all explicitly OUT OF SCOPE here.
§7 — Explicit Non-Scope (credential-specific)
NOT done here: role creation, GRANT, password mint, .env edit, login
enablement, member/group binding, RLS, Directus role/policy/permission
change, any connection as any writer, any test auth. This document is a
specification of intent only. The credential cycle is a future, separately
authorized, GPT-gated chain.
§8 — Open Decisions for GPT
OD-3 (restated) writer principal count: B=2 (recommended) vs A=1 vs C=3.
OD-6 cut_change_set.verifier_signature_id: leave NULL (recommended,
preserves CUT-writes-only-CUT) vs VERIFY back-fills that one FK.
OD-CR-1 secret-custody substrate: confirm /opt/incomex/docker/.env-pattern
vs a dedicated secrets store is acceptable for the later cycle.
End of v0.4 credential & principal strategy design (design only; no credential created).