KB-2BA3

03 — Authorization Build-Ready Substrate Design (SB-0 / L3)

16 min read Revision 1
one-roof-governanceaxisauthorizationproposalhardeningbuild-readydesign-onlyread-only2026-06-02auth-substrate

03 — Authorization Build-Ready Substrate Design (SB-0 / L3)

Package: one-roof-axis-auth-proposal-operational-hardening-build-ready-design-2026-06-02 Mode: DESIGN ONLY · READ-ONLY · NO COMMIT · NO MUTATION · paper DDL only — nothing created Hardens GPT review gap #1 to CREATE-TABLE level. Behaviour is in doc 02. Naming: this substrate is SB-0 (the new first build step; see doc 11/12). It is additive: no existing table is ALTERed, no existing CHECK is widened.


3.0 Scope & invariants

SB-0 = the smallest substrate that makes L3 technical build-authorization real and non-forgeable:

  1. one table governance_build_authorization (the grant);
  2. one view v_build_auth_valid (the recomputed validity, the anti-forgery keystone);
  3. a small set of apr_action_types rows (the vocabulary, data not schema);
  4. one design-only verifier function fn_build_commit_allowed (the gate).

Reuses for the decision layer (L0–L2): approval_requests, apr_approvals, apr_action_types, fn_apr_quorum_check (all live, doc 01 §1.3). Reserves os_proposal_approvals for L4 only.

Hard constraints (from live audit + Điều 37): additive-only; no ALTER of governance_relations/approval_requests/apr_action_types schema; no-hardcode (risk/level/allowlist are rows); no local island (writes route the audit/event spine).


3.1 governance_build_authorization — column-level design (paper DDL)

Additive new table. Types chosen to match live conventions (text codes like approval_requests.code; jsonb payloads; timestamptz).

Column Type Null Default Constraint / notes
auth_code text NOT NULL PK. Human-readable, e.g. BUILD-AUTH-NNN (assign via existing code-assignment convention; see F-83-1 note §3.6).
request_ref text NOT NULL FK → approval_requests.code. The backing L2 request.
approval_ref jsonb NOT NULL Immutable snapshot of the quorum at grant time (approver ids/types/decisions/timestamps). Evidence, not the source of validity (validity is recomputed — INV-6).
step_name text NOT NULL Exactly ONE build step (e.g. SB-12, SB-2, axis_registry). No multi-step/blanket grants.
scope jsonb NOT NULL {objects_allowed:[], objects_forbidden:[], channel, txn_strategy, max_objects}. The blast-radius contract the verifier/operator honors.
risk_level text NOT NULL CHECK ∈ (low,medium,high,sovereign). Mirrors apr_action_types.risk_level of the request's action.
commit_allowed boolean NOT NULL false Granter intent flag. Necessary but not sufficient — the view still recomputes quorum/TTL/etc.
requires_sovereign_esign boolean NOT NULL false Set true by escalation triggers (doc 02 §2.5).
sovereign_esign_ref text NULL Ref to the L4 e-sign row (interim os_proposal_approvals.id; target gov sovereign collection). CHECK: required iff requires_sovereign_esign ((requires_sovereign_esign = false) OR (sovereign_esign_ref IS NOT NULL)).
rollback_plan_ref text NOT NULL Ref to the rollback runbook/change-set for this step (doc 16). No grant without a rollback plan.
granted_by text NOT NULL Identity of the build owner who granted. Must differ from the consuming agent (INV-5; enforced in verifier + view, not a table CHECK since the consumer is unknown at grant time).
granted_at timestamptz NOT NULL now()
expires_at timestamptz NOT NULL TTL window end (default granted_at + interval '48 hours', set per risk). CHECK expires_at > granted_at.
consumed_at timestamptz NULL Set in the COMMIT txn. Single-use (INV-9).
consumed_by text NULL Identity that consumed. CHECK: set iff consumed_at set.
revoked_at timestamptz NULL Revocation time.
revoked_by text NULL
revoked_reason text NULL CHECK: revoked_by/revoked_reason set iff revoked_at set.
status text NOT NULL 'draft' CHECK ∈ (draft,active,consumed,expired,revoked). Display/lifecycle only — never the COMMIT authority (INV-10).
evidence jsonb NULL Rehearsal/preflight evidence, links to docs.
created_by text NOT NULL Authoring identity (agent may be created_by for a draft; cannot be granted_by).
created_at timestamptz NOT NULL now()

Constraints summary:

  • PK(auth_code); FK(request_ref)→approval_requests(code).
  • CHECK risk_level / status enums.
  • CHECK expires_at > granted_at.
  • CHECK sovereign-esign presence; CHECK consumed/revoked field-pairing.
  • Partial UNIQUE index uq_one_active_grant_per_step: UNIQUE (step_name) WHERE status='active' AND consumed_at IS NULL AND revoked_at IS NULL — at most one live grant per step at a time (prevents grant-stacking).
  • Index on (request_ref), (status), (expires_at) for the validity view and the expiry sweep.
  • Trigger-less by default. (If a birth/audit trigger is later attached, it must pass the code column — see F-83-1, §3.6.)

3.2 v_build_auth_valid — the anti-forgery keystone (paper SQL)

The view recomputes validity from the backing quorum; it never trusts governance_build_authorization.status. This is the single mechanism that makes a forged row inert (INV-6).

CREATE VIEW v_build_auth_valid AS
SELECT g.auth_code, g.step_name, g.scope, g.risk_level,
       g.commit_allowed, g.granted_by, g.requires_sovereign_esign,
       g.sovereign_esign_ref, g.expires_at
FROM   governance_build_authorization g
JOIN   approval_requests ar      ON ar.code = g.request_ref
WHERE  g.commit_allowed       = true
  AND  g.consumed_at IS NULL
  AND  g.revoked_at  IS NULL
  AND  now() < g.expires_at                              -- INV-11 TTL
  AND  ar.status = 'approved'                            -- backing decision exists
  AND  quorum_passed(g.request_ref)                      -- INV-6 recomputed quorum
  AND  ( g.requires_sovereign_esign = false
         OR valid_sovereign_esign(g.sovereign_esign_ref) ); -- INV-7 L4 branch

Helper predicates (design-only; specify exactly so they are buildable — GPT "key claim to verify"):

-- quorum_passed(request_code): re-derives the SAME rule fn_apr_quorum_check enforces,
-- as a read-only boolean, so the view and the trigger can never disagree.
quorum_passed(code) :=
  WITH r AS (SELECT proposed_action_code,
                    (source_context->>'proposer') AS proposer
             FROM approval_requests WHERE code = $code),
       lvl AS (SELECT risk_level FROM apr_action_types
               WHERE action_code = (SELECT proposed_action_code FROM r)),
       votes AS (SELECT approver, approver_type, decision
                 FROM apr_approvals a JOIN approval_requests ar ON ar.id=a.apr_id
                 WHERE ar.code = $code
                   AND a.approver <> (SELECT proposer FROM r))   -- INV-1 self-exclusion
  SELECT CASE (SELECT risk_level FROM lvl)
           WHEN 'high'   THEN (count(*) FILTER (WHERE decision='approve' AND approver_type='president')   >= 1)
                          AND (count(*) FILTER (WHERE decision='approve' AND approver_type='ai_council')  >= 2)
           WHEN 'medium' THEN (count(*) FILTER (WHERE decision='approve' AND approver_type='president')   >= 1)
           WHEN 'low'    THEN (count(*) FILTER (WHERE decision='approve')                                  >= 1)
         END
         AND count(*) FILTER (WHERE decision='reject') = 0      -- INV-3
  FROM votes;

-- valid_sovereign_esign(ref): READ-ONLY check of a sovereign e-sign row.
-- Interim target = os_proposal_approvals; verifies signature fields populated,
-- esignature_agreement=true, signer = registered sovereign identity, scope matches the act.
-- An agent may only SELECT this; never INSERT/UPDATE (INV-8).

The hardening rule (tested invariant, doc 02 INV-10): no application/agent path may SELECT … FROM governance_build_authorization WHERE status='active' to authorize a COMMIT. All COMMIT decisions go through v_build_auth_valid / fn_build_commit_allowed. A coverage detector (doc 09) flags any code/DOT that reads the raw status for an authorization decision.


3.3 New apr_action_types rows (DATA — Phase-A fail-closed)

Authorization needs vocabulary. These are rows, not enums/schema (no-hardcode). All land Phase-A: handler_ref='unimplemented', status='active', so the vocabulary exists and quorum can pass, but apply is blocked by fn_apr_block_unimplemented_handler until a Phase-B handler is authored under its own authorization. This copies the live precedent of amend_law/enact_nrm (doc 01 §1.3).

action_code risk_level handler_ref Purpose Level
authorize_build_step high unimplemented Issue an L3 governance_build_authorization for one step L2→L3
activate_event_type high unimplemented Flip a registered event type active=false→true (SB-11) L3
register_axis high unimplemented Add/activate an axis_registry row (doc 04/05) L2→L3
register_topic_node high unimplemented Promote a taxonomy FAC-08 candidate to active/born (doc 07) L2→L3
assign_governance_owner high unimplemented SB-2 ownership write L2
grant_governance_exception high unimplemented SB-2 governed exception L2
delegate_authority high unimplemented SB-2 delegated owner (TTL'd) L2
assign_axis_owner high unimplemented Axis owner-per-scope L2

The first four are SB-0/axis vocabulary (new in this package); the last four are the SB-1 governance action types that prior memory described — confirmed unbuilt by the live audit (doc 01 §1.3). All eight should be authored together as the governance action vocabulary, under one L2 approval, in the SB-0/SB-1 window.

Convention (INV-2): every APR using these actions sets action='review' (never 'add').


3.4 fn_build_commit_allowed — the verifier function (design-only)

-- DESIGN ONLY. Read-only. Returns ALLOW/DENY + reason. Writes nothing.
fn_build_commit_allowed(p_step text, p_action_code text, p_agent text)
RETURNS TABLE(decision text, reason text) AS $$
  -- 1. classify level from apr_action_types.risk_level(p_action_code) + reversibility(proposed_action)
  -- 2. L0: ALLOW iff p_action_code in governed allowlist AND risk='low' AND handler implemented
  -- 3. L1: ALLOW iff request approved AND apr_approvals approve with approver<>proposer
  -- 4. L2: ALLOW iff quorum_passed(request)
  -- 5. L3: ALLOW iff EXISTS(SELECT 1 FROM v_build_auth_valid
  --                         WHERE step_name=p_step AND granted_by<>p_agent)   -- INV-5
  -- 6. L4: ALLOW iff (L3) AND valid_sovereign_esign(grant.sovereign_esign_ref)
  -- default DENY (fail-closed)
$$ LANGUAGE sql STABLE;     -- STABLE/read-only: the function never mutates

consumed_at is not written by this function; it is written by the COMMIT transaction that performs the step (so the check stays read-only and idempotent).


  • request_refapproval_requests.code (live).
  • approval_ref (jsonb) snapshots apr_approvals rows at grant time (evidence).
  • sovereign_esign_ref:
    • Interim: points at os_proposal_approvals.id (char). valid_sovereign_esign checks the signature fields + esignature_agreement=true + signer = registered sovereign identity + scope match. Agent: SELECT only.
    • Target (doc 12 patch P3, a council L2/L4 decision): a purpose-built governance_sovereign_esign collection so the sales os_proposal_* module returns to sales. sovereign_esign_ref then points there. The FK target is deliberately left as a soft text ref (not a hard FK) so the interim→target migration does not require an ALTER.
  • rollback_plan_ref → the rollback runbook/change-set id (doc 16 mechanism).

3.6 F-83-1 build-time prerequisite (carried, now applies to SB-0 too)

fn_birth_registry_auto is argless and fires AFTER INSERT on apr_action_types via trg_birth_apr_action_types (doc 01 §1.5). Inserting the §3.3 action-type rows will hit the F-83-1 hazard (NULL entity_codebirth_registry NOT-NULL violation). Therefore the SB-0/SB-1 build txn must, before any apr_action_types INSERT, re-wire the trigger:

DROP TRIGGER trg_birth_apr_action_types ON apr_action_types;
CREATE TRIGGER trg_birth_apr_action_types AFTER INSERT ON apr_action_types
  FOR EACH ROW EXECUTE FUNCTION fn_birth_registry_auto('action_code');   -- mirrors approval_requests('code')

governance_build_authorization itself is trigger-less at birth, so creating the table does not hit F-83-1; only inserting the action-type rows does. auth_code assignment uses the existing code-assignment convention (no argless-birth dependency).


3.7 Build classification & order placement

  • SB-0 is the new FIRST build step, but it is gated on the one-time L2 council + L4 sovereign ratification of the authorization model (the bootstrap, doc 02 §2.7). It cannot self-authorize.
  • Once SB-0 exists, the order becomes: ratify-auth-model (L2+L4) → build SB-0 → SB-12 → SB-13 → SB-10 → SB-11(inactive) → SB-2 → SB-1, each subsequent step L3-authorized through SB-0 (a governance_build_authorization per step).
  • Transaction strategy: SB-0 itself = one small txn (table + view + indexes), then the action-type rows in the SB-1 txn (so the F-83-1 re-wire and the row INSERTs are atomic). Per-step small txns thereafter (doc 11).

3.8 Rehearsal plan (design-only; BEGIN..ROLLBACK; 5 negative tests)

To be run later in author-mode (psql -U workflow_admin, BEGIN..ROLLBACK, entry==exit, zero COMMIT). The verifier must DENY all five forgeries:

  1. Forged grant, no backing quorum: INSERT a governance_build_authorization row status='active', commit_allowed=true with a request_ref that has no passing quorum → v_build_auth_valid returns 0 rows ⇒ DENY (INV-6). ✔ proves the keystone.
  2. Expired: expires_at < now() ⇒ DENY (INV-11).
  3. Consumed: consumed_at set ⇒ DENY (INV-9).
  4. Revoked: revoked_at set ⇒ DENY (INV-12).
  5. Self-grant: granted_by = consuming agent ⇒ verifier DENY (INV-5). Plus: raw-status bypass — confirm a status='active' row with a failing quorum is NOT returned by the view (anti-INV-10 regression).

3.9 Open decisions & forbidden-compliance

Open (explicit, none hidden):

  • O-AUTH-1: interim os_proposal_approvals vs purpose-built governance_sovereign_esign (council L2/L4 — doc 12 P3).
  • O-AUTH-2: exact TTL per risk tier (default 48h; council may set per-tier).
  • O-AUTH-3: the precise sovereignty-threshold ceiling (N production objects; whether any birth_registry write always escalates). Recommend: any write to birth_registry truth or law status ⇒ L4.
  • O-AUTH-4: whether granted_by≠agent should also be a deferred table CHECK at consume-time (currently verifier-enforced; recommend keep in verifier + add a consume-time trigger when SB-0 Phase-B handler is built).

Forbidden-compliance: paper DDL only; nothing created/altered; no row inserted into any table; no quorum, grant, e-sign, event, DOT, or law mutation; os_proposal_approvals not written; read-only audit only.

Back to Knowledge Hub knowledge/dev/reports/architecture/one-roof-axis-auth-proposal-operational-hardening-build-ready-design-2026-06-02/03-authorization-build-ready-substrate-design.md