BLOCKER A - PostgreSQL Owner Semantics and G-NOLEGACY Phase Fix
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_atNULL; 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_ownerfor every executable sealed-set member (STUB_FAIL_CLOSED + REVOKE_ONLY). This moves the implicit owner privilege OFFdirectusto the NOLOGIN, non-superuser, ungrantedqt001_cp_owner. This is the step that makes effective EXECUTE removal possible - it precedes the REVOKE. - S15.2
REVOKE EXECUTEfrom PUBLIC /directus/ every role exceptqt001_cp_ownerover 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.
directusis now a non-owner AND non-superuser, so its effective EXECUTE is genuinely 0.qt001_cp_ownerowner-implicit EXECUTE is held by an unreachable NOLOGIN principal. The superuserworkflow_adminis 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.
- S15.1
- 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
- Do not require owner effective EXECUTE = 0 before ownership transfer. → POST (S15.3) runs AFTER the transfer (S15.1).
- Do not let
directusremain owner of routines where its effective EXECUTE must be 0. → S15.1 transfers ownership offdirectus. - G-NOLEGACY-PRE must not require the impossible POST state. → PRE is a structural/closure + completeness proof and does NOT require EXECUTE already revoked.
- 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.