\set ON_ERROR_STOP on -- One-time GCP staging provisioning for Leo's Cloud SQL identity. Run this -- only through ops/provision_gcp_leoclean_runtime_role.sh; psql's \password -- command encrypts the supplied value client-side, so cleartext is never SQL. -- -- The login can read the canonical allowlist and can stage a pending proposal -- through one narrowly-scoped SECURITY DEFINER function. It cannot inherit or -- SET ROLE into another principal, forge the proposer identity, write directly -- to canonical/staging tables, or inspect the legacy teleo_restore audit schema. -- Keep the runtime unable to authenticate until both database phases have -- completed and verified. Any failure before the final transaction therefore -- leaves a disabled role instead of a partially provisioned login. select 'create role leoclean_kb_runtime nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit 8' where not exists (select 1 from pg_catalog.pg_roles where rolname = 'leoclean_kb_runtime') \gexec select 'create role leoclean_kb_stage_owner nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit -1' where not exists (select 1 from pg_catalog.pg_roles where rolname = 'leoclean_kb_stage_owner') \gexec alter role leoclean_kb_runtime with nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit 8; alter role leoclean_kb_stage_owner with nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit -1 password null; -- Snapshot every non-superuser principal connected to either scoped role before -- removing the direct membership edges. Existing sessions can retain an -- already-selected SET ROLE identity after NOLOGIN/revocation, so all other -- connected sessions must be terminated before password or privilege work. create temporary table leoclean_fenced_roles ( role_oid oid primary key, role_name name unique not null ) on commit preserve rows; with recursive connected(role_oid) as ( values ('leoclean_kb_runtime'::pg_catalog.regrole::oid), ('leoclean_kb_stage_owner'::pg_catalog.regrole::oid) union select membership.member from connected join pg_catalog.pg_auth_members membership on membership.roleid = connected.role_oid ) insert into pg_temp.leoclean_fenced_roles (role_oid, role_name) select role_row.oid, role_row.rolname from connected join pg_catalog.pg_roles role_row on role_row.oid = connected.role_oid where not role_row.rolsuper; -- NOINHERIT does not prevent SET ROLE. Remove every direct membership edge in -- either direction before terminating the snapshotted sessions, closing the -- race for newly connected membership principals. select format('revoke %I from %I', granted_role.rolname, member_role.rolname) from pg_catalog.pg_auth_members membership join pg_catalog.pg_roles granted_role on granted_role.oid = membership.roleid join pg_catalog.pg_roles member_role on member_role.oid = membership.member where granted_role.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner') or member_role.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner') \gexec do $session_fence$ declare target record; remaining text; begin for target in select activity.pid from pg_catalog.pg_stat_activity activity join pg_temp.leoclean_fenced_roles fenced on fenced.role_name = activity.usename where activity.pid <> pg_catalog.pg_backend_pid() and activity.backend_type = 'client backend' loop if not pg_catalog.pg_terminate_backend(target.pid, 5000) then raise exception 'could not terminate a pre-existing scoped-role session'; end if; end loop; select pg_catalog.string_agg(activity.pid::text, ',' order by activity.pid) into remaining from pg_catalog.pg_stat_activity activity join pg_temp.leoclean_fenced_roles fenced on fenced.role_name = activity.usename where activity.pid <> pg_catalog.pg_backend_pid() and activity.backend_type = 'client backend'; if remaining is not null then raise exception 'pre-existing scoped-role sessions remain after termination'; end if; select pg_catalog.string_agg(prepared.gid, ',' order by prepared.gid) into remaining from pg_catalog.pg_prepared_xacts prepared join pg_temp.leoclean_fenced_roles fenced on fenced.role_name = prepared.owner; if remaining is not null then raise exception 'scoped-role prepared transactions must be resolved before provisioning'; end if; end $session_fence$; -- This is deliberately after the autocommitted NOLOGIN/session/membership -- fence. Interruption can leave a disabled role, never a still-enabled old -- login or an old connected session. The wrapper supplies the two prompts. \password leoclean_kb_runtime alter role leoclean_kb_runtime reset all; alter role leoclean_kb_stage_owner reset all; drop table pg_temp.leoclean_fenced_roles; select pg_catalog.format( 'alter role %I in database %I reset all', role_row.rolname, database_row.datname ) from pg_catalog.pg_db_role_setting setting_row join pg_catalog.pg_roles role_row on role_row.oid = setting_row.setrole join pg_catalog.pg_database database_row on database_row.oid = setting_row.setdatabase where role_row.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner') \gexec -- PostgreSQL normally grants TEMP to PUBLIC. A role-specific REVOKE cannot -- override that inherited grant, while REVOKE TEMP FROM PUBLIC would be a -- database-wide change that could break kb_apply/review or operator workflows. -- Leave PUBLIC TEMP policy unchanged here and surface it in the final receipt. -- TEMP cannot shadow this SECURITY DEFINER boundary: pg_catalog is first, -- pg_temp is explicitly last, and every relation reference is schema-qualified. -- Database CREATE, direct TEMP grants, and all unexpected direct database ACLs -- are rejected below. A database-wide TEMP removal requires a separate role-use -- inventory and explicit grants to every workload that still needs it. alter role leoclean_kb_runtime in database teleo_canonical reset all; alter role leoclean_kb_stage_owner in database teleo_canonical reset all; alter role leoclean_kb_runtime in database teleo_kb reset all; alter role leoclean_kb_stage_owner in database teleo_kb reset all; alter role leoclean_kb_runtime in database teleo_canonical set search_path = pg_catalog, public, kb_stage; alter role leoclean_kb_runtime in database teleo_canonical set statement_timeout = '15s'; alter role leoclean_kb_runtime in database teleo_canonical set lock_timeout = '2s'; \connect teleo_canonical begin; -- CONNECT granted to PUBLIC cannot be denied to one role with a role-specific -- REVOKE. Preserve every existing non-scoped principal's effective CONNECT -- set as explicit ACLs, remove the cluster-wide PUBLIC grant from every -- database (including templates), and then grant the runtime its sole target. -- This runs in the same transaction as every remaining privilege/topology -- assertion, so a later failure restores the original ACLs atomically. create temporary table leoclean_preserved_database_connect ( role_name name not null, database_name name not null, primary key (role_name, database_name) ) on commit drop; insert into pg_temp.leoclean_preserved_database_connect (role_name, database_name) select role_row.rolname, database_row.datname from pg_catalog.pg_roles role_row cross join pg_catalog.pg_database database_row where role_row.rolname not in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner') and pg_catalog.has_database_privilege(role_row.oid, database_row.oid, 'CONNECT'); select pg_catalog.format('revoke connect on database %I from public', database_row.datname) from pg_catalog.pg_database database_row \gexec select pg_catalog.format( 'revoke all privileges on database %I from leoclean_kb_runtime, leoclean_kb_stage_owner', database_row.datname ) from pg_catalog.pg_database database_row \gexec select pg_catalog.format( 'grant connect on database %I to %I', preserved.database_name, preserved.role_name ) from pg_temp.leoclean_preserved_database_connect preserved order by preserved.database_name, preserved.role_name \gexec grant connect on database teleo_canonical to leoclean_kb_runtime; -- The deploy principal needs temporary ownership authority to replace an -- existing function on idempotent reruns. This membership is transaction-local -- and is revoked before verification/commit. select format('grant leoclean_kb_stage_owner to %I', current_user) where current_user <> 'leoclean_kb_stage_owner' \gexec revoke all on schema public, kb_stage from leoclean_kb_runtime, leoclean_kb_stage_owner; revoke all privileges on all tables in schema public, kb_stage from leoclean_kb_runtime, leoclean_kb_stage_owner; revoke all privileges on all sequences in schema public, kb_stage from leoclean_kb_runtime, leoclean_kb_stage_owner; revoke all privileges on all functions in schema public, kb_stage from leoclean_kb_runtime, leoclean_kb_stage_owner; revoke all privileges on all procedures in schema public, kb_stage from leoclean_kb_runtime, leoclean_kb_stage_owner; -- Built-in defaults also grant PUBLIC EXECUTE on newly created application -- routines. Preserve every existing non-scoped role's effective access, -- remove PUBLIC/scoped execution from the complete application routine set, -- and restore the inventory explicitly. The reviewed staging function is -- normalized again after CREATE OR REPLACE below. create temporary table leoclean_preserved_app_routine_execute ( role_name name not null, function_oid oid not null, primary key (role_name, function_oid) ) on commit drop; insert into pg_temp.leoclean_preserved_app_routine_execute (role_name, function_oid) select role_row.rolname, function_row.oid from pg_catalog.pg_roles role_row cross join pg_catalog.pg_proc function_row join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace where role_row.rolname not in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner') and namespace.nspname in ('public', 'kb_stage') and pg_catalog.has_function_privilege(role_row.oid, function_row.oid, 'EXECUTE'); select pg_catalog.format( 'revoke execute on %s %I.%I(%s) from public, leoclean_kb_runtime, leoclean_kb_stage_owner', case when function_row.prokind = 'p' then 'procedure' else 'function' end, namespace.nspname, function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid) ) from pg_catalog.pg_proc function_row join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace where namespace.nspname in ('public', 'kb_stage') \gexec select pg_catalog.format( 'grant execute on %s %I.%I(%s) to %I', case when function_row.prokind = 'p' then 'procedure' else 'function' end, namespace.nspname, function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid), preserved.role_name ) from pg_temp.leoclean_preserved_app_routine_execute preserved join pg_catalog.pg_proc function_row on function_row.oid = preserved.function_oid join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace order by namespace.nspname, function_row.proname, preserved.role_name \gexec select pg_catalog.format( 'revoke all privileges on large object %s from leoclean_kb_runtime, leoclean_kb_stage_owner', metadata.oid ) from pg_catalog.pg_largeobject_metadata metadata \gexec -- Default PostgreSQL grants EXECUTE on built-in large-object mutators to -- PUBLIC, which would let the runtime persist data despite all relation ACLs. -- Snapshot and explicitly preserve every existing non-scoped role's effective -- access, then remove PUBLIC and both scoped roles from every mutator overload. create temporary table leoclean_preserved_lo_execute ( role_name name not null, function_oid oid not null, primary key (role_name, function_oid) ) on commit drop; insert into pg_temp.leoclean_preserved_lo_execute (role_name, function_oid) select role_row.rolname, function_row.oid from pg_catalog.pg_roles role_row cross join pg_catalog.pg_proc function_row join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace where role_row.rolname not in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner') and namespace.nspname = 'pg_catalog' and function_row.proname in ( 'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import', 'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite' ) and pg_catalog.has_function_privilege(role_row.oid, function_row.oid, 'EXECUTE'); select pg_catalog.format( 'revoke execute on function pg_catalog.%I(%s) from public, leoclean_kb_runtime, leoclean_kb_stage_owner', function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid) ) from pg_catalog.pg_proc function_row join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace where namespace.nspname = 'pg_catalog' and function_row.proname in ( 'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import', 'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite' ) \gexec select pg_catalog.format( 'grant execute on function pg_catalog.%I(%s) to %I', function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid), preserved.role_name ) from pg_temp.leoclean_preserved_lo_execute preserved join pg_catalog.pg_proc function_row on function_row.oid = preserved.function_oid order by function_row.proname, preserved.role_name \gexec select pg_catalog.format( 'revoke all privileges on parameter %I from leoclean_kb_runtime, leoclean_kb_stage_owner', parameter_acl.parname ) from pg_catalog.pg_parameter_acl parameter_acl \gexec -- Table-level REVOKE does not clear column ACLs. Clear every column ACL for both -- principals before rebuilding the exact allowlist below. select format( 'revoke all privileges (%s) on table %I.%I from %I', pg_catalog.string_agg(pg_catalog.quote_ident(attribute.attname), ', ' order by attribute.attnum), namespace.nspname, relation.relname, target.rolname ) from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid cross join ( values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner') ) as target(rolname) where namespace.nspname in ('public', 'kb_stage') and relation.relkind in ('r', 'p', 'v', 'm', 'f') and attribute.attnum > 0 and not attribute.attisdropped group by namespace.nspname, relation.relname, target.rolname \gexec grant usage on schema public, kb_stage to leoclean_kb_runtime; -- Bind runtime reads to the exact 92 columns used by the reviewed adapter. -- Table-level SELECT is deliberately absent so later columns do not become -- readable before this manifest and the adapter are reviewed together. create temporary table leoclean_runtime_read_column_allowlist ( schema_name name not null, relation_name name not null, column_name name not null, column_ordinal smallint not null, primary key (schema_name, relation_name, column_name), unique (schema_name, relation_name, column_ordinal) ) on commit drop; insert into pg_temp.leoclean_runtime_read_column_allowlist ( schema_name, relation_name, column_name, column_ordinal ) values ('public', 'agents', 'id', 1), ('public', 'agents', 'handle', 2), ('public', 'claims', 'id', 1), ('public', 'claims', 'type', 2), ('public', 'claims', 'text', 3), ('public', 'claims', 'status', 4), ('public', 'claims', 'confidence', 5), ('public', 'claims', 'tags', 6), ('public', 'claims', 'superseded_by', 7), ('public', 'claims', 'created_at', 8), ('public', 'claims', 'updated_at', 9), ('public', 'claim_evidence', 'claim_id', 1), ('public', 'claim_evidence', 'source_id', 2), ('public', 'claim_evidence', 'role', 3), ('public', 'claim_evidence', 'weight', 4), ('public', 'claim_edges', 'id', 1), ('public', 'claim_edges', 'from_claim', 2), ('public', 'claim_edges', 'to_claim', 3), ('public', 'claim_edges', 'edge_type', 4), ('public', 'sources', 'id', 1), ('public', 'sources', 'source_type', 2), ('public', 'sources', 'url', 3), ('public', 'sources', 'storage_path', 4), ('public', 'sources', 'excerpt', 5), ('public', 'sources', 'hash', 6), ('public', 'personas', 'agent_id', 1), ('public', 'personas', 'name', 2), ('public', 'personas', 'voice', 3), ('public', 'personas', 'role', 4), ('public', 'personas', 'source_ref', 5), ('public', 'personas', 'lens', 6), ('public', 'strategies', 'agent_id', 1), ('public', 'strategies', 'diagnosis', 2), ('public', 'strategies', 'guiding_policy', 3), ('public', 'strategies', 'proximate_objectives', 4), ('public', 'strategies', 'version', 5), ('public', 'strategies', 'active', 6), ('public', 'beliefs', 'agent_id', 1), ('public', 'beliefs', 'level', 2), ('public', 'beliefs', 'statement', 3), ('public', 'beliefs', 'falsifier', 4), ('public', 'beliefs', 'rank', 5), ('public', 'beliefs', 'status', 6), ('public', 'blindspots', 'agent_id', 1), ('public', 'blindspots', 'name', 2), ('public', 'blindspots', 'description', 3), ('public', 'blindspots', 'correction', 4), ('public', 'blindspots', 'kind', 5), ('public', 'blindspots', 'status', 6), ('public', 'agent_roles', 'agent_id', 1), ('public', 'agent_roles', 'title', 2), ('public', 'agent_roles', 'description', 3), ('public', 'peer_models', 'subject_id', 1), ('public', 'peer_models', 'peer_id', 2), ('public', 'peer_models', 'domain', 3), ('public', 'peer_models', 'outranks_on', 4), ('public', 'peer_models', 'deference_rule', 5), ('public', 'behavioral_rules', 'agent_id', 1), ('public', 'behavioral_rules', 'category', 2), ('public', 'behavioral_rules', 'rule', 3), ('public', 'behavioral_rules', 'rationale', 4), ('public', 'contributor_rules', 'agent_id', 1), ('public', 'contributor_rules', 'name', 2), ('public', 'contributor_rules', 'directive', 3), ('public', 'contributor_rules', 'ci_tier', 4), ('public', 'contributor_rules', 'weighting', 5), ('public', 'contributor_rules', 'rationale', 6), ('public', 'reasoning_tools', 'agent_id', 1), ('public', 'reasoning_tools', 'name', 2), ('public', 'reasoning_tools', 'description', 3), ('public', 'reasoning_tools', 'category', 4), ('public', 'governance_gates', 'agent_id', 1), ('public', 'governance_gates', 'name', 2), ('public', 'governance_gates', 'criteria', 3), ('public', 'governance_gates', 'evidence_bar', 4), ('public', 'governance_gates', 'pass_condition', 5), ('kb_stage', 'kb_proposals', 'id', 1), ('kb_stage', 'kb_proposals', 'proposal_type', 2), ('kb_stage', 'kb_proposals', 'status', 3), ('kb_stage', 'kb_proposals', 'proposed_by_handle', 4), ('kb_stage', 'kb_proposals', 'proposed_by_agent_id', 5), ('kb_stage', 'kb_proposals', 'channel', 6), ('kb_stage', 'kb_proposals', 'source_ref', 7), ('kb_stage', 'kb_proposals', 'rationale', 8), ('kb_stage', 'kb_proposals', 'payload', 9), ('kb_stage', 'kb_proposals', 'reviewed_by_handle', 10), ('kb_stage', 'kb_proposals', 'reviewed_at', 11), ('kb_stage', 'kb_proposals', 'review_note', 12), ('kb_stage', 'kb_proposals', 'applied_by_handle', 13), ('kb_stage', 'kb_proposals', 'applied_at', 14), ('kb_stage', 'kb_proposals', 'created_at', 15), ('kb_stage', 'kb_proposals', 'updated_at', 16); select pg_catalog.format( 'grant select (%s) on table %I.%I to leoclean_kb_runtime', pg_catalog.string_agg( pg_catalog.quote_ident(allowlist.column_name), ', ' order by allowlist.column_ordinal ), allowlist.schema_name, allowlist.relation_name ) from pg_temp.leoclean_runtime_read_column_allowlist allowlist group by allowlist.schema_name, allowlist.relation_name order by allowlist.schema_name, allowlist.relation_name \gexec -- The function owner is a dedicated NOLOGIN role. Grant only the columns the -- function reads/inserts. CREATE is temporary and is revoked immediately after -- ownership is assigned. grant usage, create on schema kb_stage to leoclean_kb_stage_owner; grant usage on schema public to leoclean_kb_stage_owner; grant select (id, handle) on public.agents to leoclean_kb_stage_owner; grant select on kb_stage.kb_proposals to leoclean_kb_stage_owner; grant insert ( proposal_type, status, proposed_by_handle, proposed_by_agent_id, channel, source_ref, rationale, payload ) on kb_stage.kb_proposals to leoclean_kb_stage_owner; -- Remove the prior caller-supplied-identity overload before installing the -- session-bound form. A runtime caller can only stage as canonical agent Leo. drop function if exists kb_stage.stage_leoclean_proposal(text, text, text, text, text, jsonb); create or replace function kb_stage.stage_leoclean_proposal( p_proposal_type text, p_channel text, p_source_ref text, p_rationale text, p_payload jsonb ) returns jsonb language plpgsql security definer set search_path = pg_catalog, pg_temp as $function$ declare staged kb_stage.kb_proposals%rowtype; proposer_id uuid; begin if session_user <> 'leoclean_kb_runtime' then raise exception 'stage_leoclean_proposal is restricted to leoclean_kb_runtime' using errcode = '42501'; end if; if p_proposal_type not in ('revise_claim', 'revise_strategy', 'add_edge') then raise exception 'unsupported Leo proposal type: %', p_proposal_type using errcode = '22023'; end if; if nullif(btrim(p_source_ref), '') is null then raise exception 'source_ref is required' using errcode = '22023'; end if; if nullif(btrim(p_rationale), '') is null then raise exception 'rationale is required' using errcode = '22023'; end if; if p_payload is null or jsonb_typeof(p_payload) <> 'object' then raise exception 'proposal payload must be a JSON object' using errcode = '22023'; end if; select agent.id into proposer_id from public.agents as agent where agent.handle = 'leo'; if proposer_id is null then raise exception 'canonical Leo agent row is required' using errcode = '23503'; end if; insert into kb_stage.kb_proposals ( proposal_type, status, proposed_by_handle, proposed_by_agent_id, channel, source_ref, rationale, payload ) values ( p_proposal_type, 'pending_review', 'leo', proposer_id, coalesce(nullif(p_channel, ''), 'telegram'), p_source_ref, p_rationale, p_payload ) returning * into staged; return to_jsonb(staged); end $function$; alter function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) owner to leoclean_kb_stage_owner; revoke create on schema kb_stage from leoclean_kb_stage_owner; -- Normalize explicit ACL entries left by CREATE OR REPLACE or an earlier grant. -- A newly-created function starts with owner EXECUTE, while CREATE OR REPLACE -- preserves an existing ACL on reruns. Revoke every scoped/default entry here -- so the first successful provision already has the same runtime-only ACL as -- every later provision. revoke all on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) from public, leoclean_kb_runtime, leoclean_kb_stage_owner; select format( 'revoke all on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) from %I', grantee.rolname ) from pg_catalog.pg_proc function_row cross join lateral pg_catalog.aclexplode( coalesce( function_row.proacl, pg_catalog.acldefault('f', function_row.proowner) ) ) as acl join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee where function_row.oid = 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)'::pg_catalog.regprocedure \gexec grant execute on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) to leoclean_kb_runtime; select format('revoke leoclean_kb_stage_owner from %I', current_user) where current_user <> 'leoclean_kb_stage_owner' \gexec do $verification$ declare unexpected text; runtime_oid oid := 'leoclean_kb_runtime'::pg_catalog.regrole::oid; owner_oid oid := 'leoclean_kb_stage_owner'::pg_catalog.regrole::oid; stage_function oid := 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)'::pg_catalog.regprocedure::oid; begin if exists ( select 1 from pg_catalog.pg_roles role_row where role_row.rolname = 'leoclean_kb_runtime' and (role_row.rolcanlogin or role_row.rolsuper or role_row.rolcreatedb or role_row.rolcreaterole or role_row.rolinherit or role_row.rolreplication or role_row.rolbypassrls or role_row.rolconnlimit <> 8) ) then raise exception 'leoclean_kb_runtime has unsafe provisioning role attributes'; end if; if exists ( select 1 from pg_catalog.pg_roles role_row where role_row.rolname = 'leoclean_kb_stage_owner' and (role_row.rolcanlogin or role_row.rolsuper or role_row.rolcreatedb or role_row.rolcreaterole or role_row.rolinherit or role_row.rolreplication or role_row.rolbypassrls or role_row.rolconnlimit <> -1) ) then raise exception 'leoclean_kb_stage_owner has unsafe role attributes'; end if; select pg_catalog.string_agg( pg_catalog.format('%I->%I', granted_role.rolname, member_role.rolname), ', ' order by granted_role.rolname, member_role.rolname ) into unexpected from pg_catalog.pg_auth_members membership join pg_catalog.pg_roles granted_role on granted_role.oid = membership.roleid join pg_catalog.pg_roles member_role on member_role.oid = membership.member where membership.roleid in (runtime_oid, owner_oid) or membership.member in (runtime_oid, owner_oid); if unexpected is not null then raise exception 'scoped Leo roles retain role memberships: %', unexpected; end if; if not exists ( select 1 from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace where namespace.nspname = 'kb_stage' and relation.relname = 'kb_proposals' and relation.relkind = 'r' and relation.relpersistence = 'p' and not relation.relrowsecurity and not relation.relforcerowsecurity ) then raise exception 'kb_stage.kb_proposals is not the reviewed permanent base table'; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, relation.relkind), ', ' order by namespace.nspname, relation.relname ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace where (namespace.nspname, relation.relname) in ( ('public', 'agents'), ('public', 'claims'), ('public', 'claim_evidence'), ('public', 'claim_edges'), ('public', 'sources'), ('public', 'personas'), ('public', 'strategies'), ('public', 'beliefs'), ('public', 'blindspots'), ('public', 'agent_roles'), ('public', 'peer_models'), ('public', 'behavioral_rules'), ('public', 'contributor_rules'), ('public', 'reasoning_tools'), ('public', 'governance_gates'), ('kb_stage', 'kb_proposals') ) and relation.relkind <> 'r'; if unexpected is not null then raise exception 'Leo read allowlist contains a non-base relation: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%s->%s', inheritance.inhparent::pg_catalog.regclass, inheritance.inhrelid::pg_catalog.regclass), ', ' ) into unexpected from pg_catalog.pg_inherits inheritance where inheritance.inhparent in ( 'public.agents'::pg_catalog.regclass, 'public.claims'::pg_catalog.regclass, 'public.claim_evidence'::pg_catalog.regclass, 'public.claim_edges'::pg_catalog.regclass, 'public.sources'::pg_catalog.regclass, 'public.personas'::pg_catalog.regclass, 'public.strategies'::pg_catalog.regclass, 'public.beliefs'::pg_catalog.regclass, 'public.blindspots'::pg_catalog.regclass, 'public.agent_roles'::pg_catalog.regclass, 'public.peer_models'::pg_catalog.regclass, 'public.behavioral_rules'::pg_catalog.regclass, 'public.contributor_rules'::pg_catalog.regclass, 'public.reasoning_tools'::pg_catalog.regclass, 'public.governance_gates'::pg_catalog.regclass, 'kb_stage.kb_proposals'::pg_catalog.regclass ) or inheritance.inhrelid in ( 'public.agents'::pg_catalog.regclass, 'public.claims'::pg_catalog.regclass, 'public.claim_evidence'::pg_catalog.regclass, 'public.claim_edges'::pg_catalog.regclass, 'public.sources'::pg_catalog.regclass, 'public.personas'::pg_catalog.regclass, 'public.strategies'::pg_catalog.regclass, 'public.beliefs'::pg_catalog.regclass, 'public.blindspots'::pg_catalog.regclass, 'public.agent_roles'::pg_catalog.regclass, 'public.peer_models'::pg_catalog.regclass, 'public.behavioral_rules'::pg_catalog.regclass, 'public.contributor_rules'::pg_catalog.regclass, 'public.reasoning_tools'::pg_catalog.regclass, 'public.governance_gates'::pg_catalog.regclass, 'kb_stage.kb_proposals'::pg_catalog.regclass ); if unexpected is not null then raise exception 'Leo read allowlist has inheritance edges: %', unexpected; end if; with expected(ordinal, attname, typname, not_null, has_default, collated) as ( values (1, 'id', 'uuid', true, true, false), (2, 'proposal_type', 'text', true, false, true), (3, 'status', 'text', true, true, true), (4, 'proposed_by_handle', 'text', false, false, true), (5, 'proposed_by_agent_id', 'uuid', false, false, false), (6, 'channel', 'text', true, true, true), (7, 'source_ref', 'text', false, false, true), (8, 'rationale', 'text', true, false, true), (9, 'payload', 'jsonb', true, false, false), (10, 'reviewed_by_handle', 'text', false, false, true), (11, 'reviewed_by_agent_id', 'uuid', false, false, false), (12, 'reviewed_at', 'timestamptz', false, false, false), (13, 'review_note', 'text', false, false, true), (14, 'applied_by_handle', 'text', false, false, true), (15, 'applied_by_agent_id', 'uuid', false, false, false), (16, 'applied_at', 'timestamptz', false, false, false), (17, 'created_at', 'timestamptz', true, true, false), (18, 'updated_at', 'timestamptz', true, true, false) ), actual as ( select attribute.attnum::int as ordinal, attribute.attname, type_row.typname, attribute.attnotnull as not_null, attribute.atthasdef as has_default, attribute.attidentity, attribute.attgenerated, attribute.atttypmod, type_namespace.nspname as type_namespace, attribute.attcollation = 'pg_catalog.default'::pg_catalog.regcollation as collated from pg_catalog.pg_attribute attribute join pg_catalog.pg_type type_row on type_row.oid = attribute.atttypid join pg_catalog.pg_namespace type_namespace on type_namespace.oid = type_row.typnamespace where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass and attribute.attnum > 0 and not attribute.attisdropped ) select pg_catalog.string_agg(coalesce(expected.attname, actual.attname), ', ' order by coalesce(expected.ordinal, actual.ordinal)) into unexpected from expected full join actual using (ordinal, attname) where expected.ordinal is null or actual.ordinal is null or expected.typname is distinct from actual.typname or expected.not_null is distinct from actual.not_null or expected.has_default is distinct from actual.has_default or expected.collated is distinct from actual.collated or actual.attidentity <> '' or actual.attgenerated <> '' or actual.atttypmod <> -1 or actual.type_namespace <> 'pg_catalog'; if unexpected is not null then raise exception 'kb_stage.kb_proposals column contract drifted: %', unexpected; end if; if exists ( select 1 from pg_catalog.pg_trigger trigger_row where trigger_row.tgrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass and not trigger_row.tgisinternal ) or exists ( select 1 from pg_catalog.pg_rewrite rewrite_row where rewrite_row.ev_class = 'kb_stage.kb_proposals'::pg_catalog.regclass ) or exists ( select 1 from pg_catalog.pg_policy policy_row where policy_row.polrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass ) or exists ( select 1 from pg_catalog.pg_attribute attribute where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass and attribute.attnum > 0 and not attribute.attisdropped and attribute.attgenerated <> '' ) then raise exception 'kb_stage.kb_proposals has unsafe trigger/rule/RLS/generated topology'; end if; with expected(attname, expression) as ( values ('id', 'gen_random_uuid()'), ('status', '''pending_review''::text'), ('channel', '''cli''::text'), ('created_at', 'now()'), ('updated_at', 'now()') ), actual as ( select attribute.attname, pg_catalog.pg_get_expr(default_row.adbin, default_row.adrelid) as expression from pg_catalog.pg_attribute attribute join pg_catalog.pg_attrdef default_row on default_row.adrelid = attribute.attrelid and default_row.adnum = attribute.attnum where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass and attribute.attnum > 0 and not attribute.attisdropped ) select pg_catalog.string_agg(coalesce(expected.attname, actual.attname), ', ') into unexpected from expected full join actual using (attname) where expected.expression is distinct from actual.expression; if unexpected is not null then raise exception 'kb_stage.kb_proposals default topology drifted: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%s:%s', dependency.refclassid::pg_catalog.regclass, dependency.refobjid), ', ' ) into unexpected from pg_catalog.pg_attrdef default_row join pg_catalog.pg_depend dependency on dependency.classid = 'pg_catalog.pg_attrdef'::pg_catalog.regclass and dependency.objid = default_row.oid where default_row.adrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass and ( dependency.refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass and dependency.refobjid not in ( 'pg_catalog.gen_random_uuid()'::pg_catalog.regprocedure, 'pg_catalog.now()'::pg_catalog.regprocedure ) or dependency.refclassid = 'pg_catalog.pg_type'::pg_catalog.regclass and not exists ( select 1 from pg_catalog.pg_type type_row join pg_catalog.pg_namespace namespace on namespace.oid = type_row.typnamespace where type_row.oid = dependency.refobjid and namespace.nspname = 'pg_catalog' ) or dependency.refclassid = 'pg_catalog.pg_operator'::pg_catalog.regclass and not exists ( select 1 from pg_catalog.pg_operator operator_row join pg_catalog.pg_namespace namespace on namespace.oid = operator_row.oprnamespace where operator_row.oid = dependency.refobjid and namespace.nspname = 'pg_catalog' ) or dependency.refclassid = 'pg_catalog.pg_collation'::pg_catalog.regclass and not exists ( select 1 from pg_catalog.pg_collation collation_row join pg_catalog.pg_namespace namespace on namespace.oid = collation_row.collnamespace where collation_row.oid = dependency.refobjid and namespace.nspname = 'pg_catalog' ) ); if unexpected is not null then raise exception 'kb_stage.kb_proposals defaults depend on unreviewed objects: %', unexpected; end if; if ( select pg_catalog.count(*) <> 6 or pg_catalog.count(*) filter ( where (constraint_row.conname, constraint_row.contype) in ( ('kb_proposals_pkey', 'p'), ('kb_proposals_proposal_type_check', 'c'), ('kb_proposals_status_check', 'c'), ('kb_proposals_applied_by_agent_id_fkey', 'f'), ('kb_proposals_proposed_by_agent_id_fkey', 'f'), ('kb_proposals_reviewed_by_agent_id_fkey', 'f') ) ) <> 6 from pg_catalog.pg_constraint constraint_row where constraint_row.conrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass ) or exists ( select 1 from pg_catalog.pg_constraint constraint_row join pg_catalog.pg_depend dependency on dependency.classid = 'pg_catalog.pg_constraint'::pg_catalog.regclass and dependency.objid = constraint_row.oid and dependency.refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass join pg_catalog.pg_proc function_row on function_row.oid = dependency.refobjid join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace where constraint_row.conrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass and namespace.nspname <> 'pg_catalog' ) or exists ( select 1 from pg_catalog.pg_index index_row where index_row.indrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass and ( index_row.indexprs is not null or ( index_row.indpred is not null and pg_catalog.pg_get_expr(index_row.indpred, index_row.indrelid) <> '(proposed_by_handle IS NOT NULL)' ) ) ) then raise exception 'kb_stage.kb_proposals constraint/index topology drifted'; end if; select pg_catalog.string_agg( pg_catalog.format( '%I:%s:%s:grantable=%s', database_row.datname, grantee.rolname, acl.privilege_type, acl.is_grantable ), ', ' order by database_row.datname, grantee.rolname, acl.privilege_type ) into unexpected from pg_catalog.pg_database database_row cross join lateral pg_catalog.aclexplode( coalesce( database_row.datacl, pg_catalog.acldefault('d', database_row.datdba) ) ) as acl join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee where grantee.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner') and not ( database_row.datname = 'teleo_canonical' and grantee.rolname = 'leoclean_kb_runtime' and acl.privilege_type = 'CONNECT' and not acl.is_grantable ); if unexpected is not null then raise exception 'scoped Leo roles retain unexpected direct database ACLs: %', unexpected; end if; select pg_catalog.string_agg(database_row.datname, ', ' order by database_row.datname) into unexpected from pg_catalog.pg_database database_row where database_row.datname <> 'teleo_canonical' and has_database_privilege('leoclean_kb_runtime', database_row.oid, 'CONNECT'); if unexpected is not null then raise exception 'runtime CONNECT reaches a noncanonical database: %', unexpected; end if; select pg_catalog.string_agg(database_row.datname, ', ' order by database_row.datname) into unexpected from pg_catalog.pg_database database_row where has_database_privilege('leoclean_kb_stage_owner', database_row.oid, 'CONNECT'); if unexpected is not null then raise exception 'stage owner CONNECT reaches a database: %', unexpected; end if; select pg_catalog.string_agg(database_row.datname, ', ' order by database_row.datname) into unexpected from pg_catalog.pg_database database_row cross join lateral pg_catalog.aclexplode( coalesce(database_row.datacl, pg_catalog.acldefault('d', database_row.datdba)) ) acl where acl.grantee = 0 and acl.privilege_type = 'CONNECT'; if unexpected is not null then raise exception 'PUBLIC retains database CONNECT: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%s:%s', setting_row.setdatabase, setting_row.setconfig), ', ' ) into unexpected from pg_catalog.pg_db_role_setting setting_row where setting_row.setrole in (runtime_oid, owner_oid) and not ( setting_row.setrole = runtime_oid and setting_row.setdatabase = ( select database_row.oid from pg_catalog.pg_database database_row where database_row.datname = 'teleo_canonical' ) and pg_catalog.cardinality(setting_row.setconfig) = 3 and setting_row.setconfig @> array[ 'search_path=pg_catalog, public, kb_stage', 'statement_timeout=15s', 'lock_timeout=2s' ]::text[] ); if unexpected is not null or not exists ( select 1 from pg_catalog.pg_db_role_setting setting_row where setting_row.setrole = runtime_oid and setting_row.setdatabase = ( select database_row.oid from pg_catalog.pg_database database_row where database_row.datname = 'teleo_canonical' ) and pg_catalog.cardinality(setting_row.setconfig) = 3 and setting_row.setconfig @> array[ 'search_path=pg_catalog, public, kb_stage', 'statement_timeout=15s', 'lock_timeout=2s' ]::text[] ) then raise exception 'scoped Leo role settings differ from the reviewed contract'; end if; select pg_catalog.string_agg( pg_catalog.format('%I:%s', database_row.datname, scoped_role.name), ', ' order by database_row.datname, scoped_role.name ) into unexpected from pg_catalog.pg_database database_row cross join (values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner')) scoped_role(name) where has_database_privilege(scoped_role.name, database_row.oid, 'CREATE'); if unexpected is not null then raise exception 'a scoped Leo role unexpectedly has database CREATE: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%s:%s:%s', dependency.classid::pg_catalog.regclass, dependency.objid, dependency.objsubid), ', ' ) into unexpected from pg_catalog.pg_shdepend dependency where dependency.refclassid = 'pg_catalog.pg_authid'::pg_catalog.regclass and dependency.refobjid = runtime_oid and dependency.deptype = 'o'; if unexpected is not null then raise exception 'leoclean_kb_runtime unexpectedly owns database objects: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%s:%s:%s', dependency.classid::pg_catalog.regclass, dependency.objid, dependency.objsubid), ', ' ) into unexpected from pg_catalog.pg_shdepend dependency where dependency.refclassid = 'pg_catalog.pg_authid'::pg_catalog.regclass and dependency.refobjid = owner_oid and dependency.deptype = 'o' and not ( dependency.dbid = (select database_row.oid from pg_catalog.pg_database database_row where database_row.datname = current_database()) and dependency.classid = 'pg_catalog.pg_proc'::pg_catalog.regclass and dependency.objid = stage_function and dependency.objsubid = 0 ); if unexpected is not null then raise exception 'leoclean_kb_stage_owner owns unexpected database objects: %', unexpected; end if; select pg_catalog.string_agg(metadata.oid::text, ', ' order by metadata.oid) into unexpected from pg_catalog.pg_largeobject_metadata metadata where metadata.lomowner in (runtime_oid, owner_oid) or exists ( select 1 from pg_catalog.aclexplode( coalesce(metadata.lomacl, pg_catalog.acldefault('L', metadata.lomowner)) ) acl where acl.grantee in (0, runtime_oid, owner_oid) and acl.privilege_type in ('SELECT', 'UPDATE') ); if unexpected is not null then raise exception 'scoped Leo roles retain large-object ownership or ACL reachability: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I(%s)', function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid)), ', ' ) into unexpected from pg_catalog.pg_proc function_row join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace where namespace.nspname = 'pg_catalog' and function_row.proname in ( 'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import', 'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite' ) and ( has_function_privilege('leoclean_kb_runtime', function_row.oid, 'EXECUTE') or has_function_privilege('leoclean_kb_stage_owner', function_row.oid, 'EXECUTE') ); if unexpected is not null then raise exception 'PUBLIC or inherited large-object mutators remain executable: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I(%s)', function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid)), ', ' order by function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid) ) into unexpected from pg_catalog.pg_proc function_row join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace cross join lateral pg_catalog.aclexplode( coalesce(function_row.proacl, pg_catalog.acldefault('f', function_row.proowner)) ) acl where namespace.nspname = 'pg_catalog' and function_row.proname in ( 'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import', 'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite' ) and acl.grantee = 0 and acl.privilege_type = 'EXECUTE'; if unexpected is not null then raise exception 'PUBLIC retains large-object mutator EXECUTE: %', unexpected; end if; select pg_catalog.string_agg(parameter_acl.parname, ', ' order by parameter_acl.parname) into unexpected from pg_catalog.pg_parameter_acl parameter_acl where has_parameter_privilege('leoclean_kb_runtime', parameter_acl.parname, 'SET') or has_parameter_privilege('leoclean_kb_runtime', parameter_acl.parname, 'ALTER SYSTEM') or has_parameter_privilege('leoclean_kb_stage_owner', parameter_acl.parname, 'SET') or has_parameter_privilege('leoclean_kb_stage_owner', parameter_acl.parname, 'ALTER SYSTEM'); if unexpected is not null then raise exception 'scoped Leo roles retain parameter privileges: %', unexpected; end if; if has_schema_privilege('leoclean_kb_runtime', 'public', 'CREATE') or has_schema_privilege('leoclean_kb_runtime', 'kb_stage', 'CREATE') or has_schema_privilege('leoclean_kb_stage_owner', 'public', 'CREATE') or has_schema_privilege('leoclean_kb_stage_owner', 'kb_stage', 'CREATE') then raise exception 'a scoped Leo role unexpectedly has schema CREATE'; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, privilege.name), ', ' order by namespace.nspname, relation.relname, privilege.name ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace cross join (values ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) privilege(name) where namespace.nspname in ('public', 'kb_stage') and relation.relkind in ('r', 'p', 'v', 'm', 'f') and has_table_privilege('leoclean_kb_runtime', relation.oid, privilege.name); if unexpected is not null then raise exception 'leoclean_kb_runtime unexpectedly has table-level DML: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I.%I:%s', namespace.nspname, relation.relname, attribute.attname, privilege.name), ', ' order by namespace.nspname, relation.relname, attribute.attnum, privilege.name ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid cross join (values ('INSERT'), ('UPDATE'), ('REFERENCES')) privilege(name) where namespace.nspname in ('public', 'kb_stage') and relation.relkind in ('r', 'p', 'v', 'm', 'f') and attribute.attnum > 0 and not attribute.attisdropped and has_column_privilege('leoclean_kb_runtime', relation.oid, attribute.attnum, privilege.name); if unexpected is not null then raise exception 'leoclean_kb_runtime unexpectedly has column-level DML: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I', namespace.nspname, relation.relname), ', ' order by namespace.nspname, relation.relname ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace where namespace.nspname in ('public', 'kb_stage') and relation.relkind in ('r', 'p', 'v', 'm', 'f') and has_table_privilege('leoclean_kb_runtime', relation.oid, 'SELECT'); if unexpected is not null then raise exception 'leoclean_kb_runtime unexpectedly has table-level SELECT: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I.%I', allowlist.schema_name, allowlist.relation_name, allowlist.column_name), ', ' order by allowlist.schema_name, allowlist.relation_name, allowlist.column_ordinal ) into unexpected from pg_temp.leoclean_runtime_read_column_allowlist allowlist left join pg_catalog.pg_namespace namespace on namespace.nspname = allowlist.schema_name left join pg_catalog.pg_class relation on relation.relnamespace = namespace.oid and relation.relname = allowlist.relation_name left join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid and attribute.attname = allowlist.column_name and attribute.attnum > 0 and not attribute.attisdropped where attribute.attnum is null or not coalesce( has_column_privilege('leoclean_kb_runtime', relation.oid, attribute.attnum, 'SELECT'), false ); if unexpected is not null then raise exception 'leoclean_kb_runtime is missing required column SELECT: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname), ', ' order by namespace.nspname, relation.relname, attribute.attnum ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid left join pg_temp.leoclean_runtime_read_column_allowlist allowlist on allowlist.schema_name = namespace.nspname and allowlist.relation_name = relation.relname and allowlist.column_name = attribute.attname where namespace.nspname in ('public', 'kb_stage') and relation.relkind in ('r', 'p', 'v', 'm', 'f') and attribute.attnum > 0 and not attribute.attisdropped and has_column_privilege('leoclean_kb_runtime', relation.oid, attribute.attnum, 'SELECT') and allowlist.column_name is null; if unexpected is not null then raise exception 'leoclean_kb_runtime has column SELECT outside its allowlist: %', unexpected; end if; if has_table_privilege('leoclean_kb_stage_owner', 'public.agents', 'SELECT') or not has_column_privilege('leoclean_kb_stage_owner', 'public.agents', 'id', 'SELECT') or not has_column_privilege('leoclean_kb_stage_owner', 'public.agents', 'handle', 'SELECT') or not has_table_privilege('leoclean_kb_stage_owner', 'kb_stage.kb_proposals', 'SELECT') or has_table_privilege('leoclean_kb_stage_owner', 'kb_stage.kb_proposals', 'INSERT') then raise exception 'leoclean_kb_stage_owner does not have the exact read/insert grant shape'; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname), ', ' order by namespace.nspname, relation.relname, attribute.attnum ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid where namespace.nspname in ('public', 'kb_stage') and relation.relkind in ('r', 'p', 'v', 'm', 'f') and attribute.attnum > 0 and not attribute.attisdropped and has_column_privilege('leoclean_kb_stage_owner', relation.oid, attribute.attnum, 'INSERT') and not ( namespace.nspname = 'kb_stage' and relation.relname = 'kb_proposals' and attribute.attname in ( 'proposal_type', 'status', 'proposed_by_handle', 'proposed_by_agent_id', 'channel', 'source_ref', 'rationale', 'payload' ) ); if unexpected is not null then raise exception 'leoclean_kb_stage_owner can INSERT unexpected columns: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, privilege.name), ', ' order by namespace.nspname, relation.relname, privilege.name ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace cross join (values ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) privilege(name) where namespace.nspname in ('public', 'kb_stage') and relation.relkind in ('r', 'p', 'v', 'm', 'f') and has_table_privilege('leoclean_kb_stage_owner', relation.oid, privilege.name); if unexpected is not null then raise exception 'leoclean_kb_stage_owner unexpectedly has table-level DML: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I.%I:%s', namespace.nspname, relation.relname, attribute.attname, privilege.name), ', ' order by namespace.nspname, relation.relname, attribute.attnum, privilege.name ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid cross join (values ('UPDATE'), ('REFERENCES')) privilege(name) where namespace.nspname in ('public', 'kb_stage') and relation.relkind in ('r', 'p', 'v', 'm', 'f') and attribute.attnum > 0 and not attribute.attisdropped and has_column_privilege('leoclean_kb_stage_owner', relation.oid, attribute.attnum, privilege.name); if unexpected is not null then raise exception 'leoclean_kb_stage_owner unexpectedly has column-level DML: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname), ', ' order by namespace.nspname, relation.relname, attribute.attnum ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid where namespace.nspname in ('public', 'kb_stage') and relation.relkind in ('r', 'p', 'v', 'm', 'f') and attribute.attnum > 0 and not attribute.attisdropped and has_column_privilege('leoclean_kb_stage_owner', relation.oid, attribute.attnum, 'SELECT') and not ( (namespace.nspname = 'kb_stage' and relation.relname = 'kb_proposals') or ( namespace.nspname = 'public' and relation.relname = 'agents' and attribute.attname in ('id', 'handle') ) ); if unexpected is not null then raise exception 'leoclean_kb_stage_owner can SELECT unexpected columns: %', unexpected; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I:%s:%s', namespace.nspname, relation.relname, scoped_role.name, privilege.name), ', ' order by namespace.nspname, relation.relname, scoped_role.name, privilege.name ) into unexpected from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace cross join (values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner')) scoped_role(name) cross join (values ('USAGE'), ('SELECT'), ('UPDATE')) privilege(name) where namespace.nspname in ('public', 'kb_stage') and relation.relkind = 'S' and has_sequence_privilege(scoped_role.name, relation.oid, privilege.name); if unexpected is not null then raise exception 'a scoped Leo role unexpectedly has sequence privileges: %', unexpected; end if; if exists ( select 1 from pg_catalog.pg_attribute attribute where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass and attribute.attname in ( 'proposal_type', 'status', 'proposed_by_handle', 'proposed_by_agent_id', 'channel', 'source_ref', 'rationale', 'payload' ) and not has_column_privilege( 'leoclean_kb_stage_owner', attribute.attrelid, attribute.attnum, 'INSERT' ) ) then raise exception 'leoclean_kb_stage_owner is missing a required INSERT column grant'; end if; select pg_catalog.string_agg( pg_catalog.format('%I.%I(%s)', namespace.nspname, function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid)), ', ' ) into unexpected from pg_catalog.pg_proc function_row join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace where namespace.nspname in ('public', 'kb_stage') and function_row.prosecdef and function_row.oid <> stage_function and has_function_privilege('leoclean_kb_runtime', function_row.oid, 'EXECUTE'); if unexpected is not null then raise exception 'leoclean_kb_runtime can execute unexpected SECURITY DEFINER functions: %', unexpected; end if; if not exists ( select 1 from pg_catalog.pg_proc function_row where function_row.oid = stage_function and function_row.prosecdef and function_row.proowner = owner_oid and function_row.proconfig = array['search_path=pg_catalog, pg_temp']::text[] ) then raise exception 'stage_leoclean_proposal owner/security/search_path contract is invalid'; end if; select pg_catalog.string_agg( pg_catalog.format( 'grantee=%s privilege=%s grantable=%s', acl.grantee, acl.privilege_type, acl.is_grantable ), ', ' ) into unexpected from pg_catalog.pg_proc function_row cross join lateral pg_catalog.aclexplode( coalesce( function_row.proacl, pg_catalog.acldefault('f', function_row.proowner) ) ) as acl where function_row.oid = stage_function and not ( acl.grantee = runtime_oid and acl.privilege_type = 'EXECUTE' and not acl.is_grantable ); if unexpected is not null then raise exception 'stage_leoclean_proposal has unexpected ACL entries: %', unexpected; end if; if not has_function_privilege( 'leoclean_kb_runtime', 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)', 'EXECUTE' ) then raise exception 'leoclean_kb_runtime cannot execute stage_leoclean_proposal'; end if; if has_function_privilege( 'leoclean_kb_stage_owner', 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)', 'EXECUTE' ) then raise exception 'leoclean_kb_stage_owner must not execute stage_leoclean_proposal'; end if; end $verification$; -- LOGIN is the last state change in the same transaction as every database ACL, -- canonical privilege, large-object, and topology assertion. Any failure rolls -- back the complete privilege migration and leaves the role NOLOGIN. alter role leoclean_kb_runtime with login nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit 8; do $login_verification$ begin if exists ( select 1 from pg_catalog.pg_roles role_row where role_row.rolname = 'leoclean_kb_runtime' and ( not role_row.rolcanlogin or role_row.rolsuper or role_row.rolcreatedb or role_row.rolcreaterole or role_row.rolinherit or role_row.rolreplication or role_row.rolbypassrls or role_row.rolconnlimit <> 8 ) ) then raise exception 'leoclean_kb_runtime final login attributes are unsafe'; end if; end $login_verification$; commit;