KB-76B5

BLOCKER A - PostgreSQL Owner Semantics and G-NOLEGACY Phase Fix

5 min read Revision 1
fix7codex-recheck-patchowner-semanticsg-nolegacy2026-06-08

02 - BLOCKER A: PostgreSQL Owner Semantics and G-NOLEGACY Phase Fix (decisive)

Codex recheck finding

At S15 the legacy routines are still owned by directus. In PostgreSQL an object owner retains implicit privileges, so REVOKE EXECUTE cannot make directus effective EXECUTE = 0 while it remains owner; S16 ownership transfer was too late. Therefore G-NOLEGACY-POST (required directus effective EXECUTE = 0) was impossible after S15, and REVOKE_ONLY routines stayed owner-callable. The guard and package order were not executable as written. (Codex recheck CHECK_B FAIL / CHECK_A NEEDS_FIX.)

Live evidence (read-only, DB directus, 2026-06-08, query_pg on pg_roles)

role rolsuper rolbypassrls rolcanlogin implication
directus false false true NON-superuser; owns the legacy routines → ownership transfer off directus + REVOKE can reach effective EXECUTE = 0
workflow_admin true true true cluster SUPERUSER; inherently bypasses ALL object ACL → cannot be made privilege-zero by ownership/ACL; must be explicitly dispositioned

This live read is decisive: because directus is non-superuser, the fix is feasible (had directus been a superuser, no ownership/ACL change could zero its EXECUTE and the design would have needed a different mechanism entirely). And the existence of a real superuser (workflow_admin) means every effective-privilege guard MUST carve out superusers, exactly as Codex demanded.

The fix - owner-isolation-first phase model (blueprint doc 04 §S15 + dependency notes)

The previous single S15 ("repoint + REVOKE + stub") is replaced by a STAGED S14 and an atomic, strictly-ordered S15 whose sub-steps obey PostgreSQL owner semantics:

  • S14 - stage + pre-cutover snapshot (NO activation). The sealed manifest stays STAGED (manifest_set.activated_at NULL; gateway fail-closed; readiness BLOCKED; live writer still on legacy). The complete effective-privilege ownership/ACL snapshot is captured here, BEFORE any owner transfer (BLOCKER H).
  • S15 - one atomic operator transaction, ordered:
    • S15.1 ALTER ... OWNER TO qt001_cp_owner for every executable sealed-set member (STUB_FAIL_CLOSED + REVOKE_ONLY). This moves the implicit owner privilege OFF directus to the NOLOGIN, non-superuser, ungranted qt001_cp_owner. This is the step that makes effective EXECUTE removal possible - it precedes the REVOKE.
    • S15.2 REVOKE EXECUTE from PUBLIC / directus / every role except qt001_cp_owner over the complete sealed set; replace ONLY STUB_FAIL_CLOSED bodies with a fail-closed stub.
    • S15.3 verify G-NOLEGACY-POST: non-superuser, non-owner effective EXECUTE = 0 over the sealed set. directus is now a non-owner AND non-superuser, so its effective EXECUTE is genuinely 0. qt001_cp_owner owner-implicit EXECUTE is held by an unreachable NOLOGIN principal. The superuser workflow_admin is EXCLUDED from the =0 claim and recorded as an accepted out-of-band property.
    • S15.4 ACTIVATE the sealed manifest (quorum + epoch) AND repoint the live writer to the #26-pinned gateway. (Activation moved here from S14 - BLOCKER D.)
    • S15.5 verify G-NOMIXED-AUTHORITY + G-WRITER-GATEWAY-IDENTITY + gateway fail-closed.
  • S16 - owner/ACL cutover of the remaining legacy relations + residual directus authority; the executable routine ownership is already done at S15.1.

The four rules Codex set - each satisfied

  1. Do not require owner effective EXECUTE = 0 before ownership transfer. → POST (S15.3) runs AFTER the transfer (S15.1).
  2. Do not let directus remain owner of routines where its effective EXECUTE must be 0. → S15.1 transfers ownership off directus.
  3. G-NOLEGACY-PRE must not require the impossible POST state. → PRE is a structural/closure + completeness proof and does NOT require EXECUTE already revoked.
  4. G-NOLEGACY-POST must be placed after the step that makes it possible. → POST is at S15.3, after S15.1 transfer + S15.2 revoke.

Guard changes (doc 06)

  • G-NOLEGACY-POST now: non-superuser, non-owner effective EXECUTE = 0; states the owner-transfer precondition; dispositions the superuser; fails if an owner still holds implicit EXECUTE.
  • Guard-quality rule 5 (new): effective-privilege guards compute over non-owner roles only and explicitly disposition superusers; an owner-effective-zero expectation without an ownership transfer is itself a defect.

Self-check

PASS only if directus effective EXECUTE can be 0 under PostgreSQL semantics. PASS - it is 0 only after S15.1 transfers ownership off the (non-superuser) directus, before S15.2 revoke and S15.3 verification. The superuser is dispositioned, not silently assumed. No phase requires an impossible state.

Back to Knowledge Hub knowledge/dev/reports/architecture/t1-fix7-blueprint-patch-after-codex-recheck-owner-semantics-2026-06-08/02-pg-owner-semantics-g-nolegacy-fix.md