@consiliency/agent-board-schema 1.64.0 → 1.74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +19 -0
  2. package/fixtures/already-applied.json +44 -4
  3. package/migrations/20261015000100_agent_board_session_provenance.sql +367 -0
  4. package/migrations/20261015000200_agent_board_request_preserve_removed.sql +87 -0
  5. package/migrations/20261015000300_agent_board_rls_correlation.sql +132 -0
  6. package/migrations/20261015000400_agent_board_resolution_authority.sql +146 -0
  7. package/migrations/20261026000100_agent_board_thread_association.sql +1287 -0
  8. package/migrations/20261101000100_agent_board_session_measured_mint.sql +145 -0
  9. package/migrations/20261104065658_agent_board_reply_delivery.sql +659 -0
  10. package/migrations/20261104170000_agent_board_tasking_replay_scope.sql +320 -0
  11. package/migrations/20261104180000_agent_board_delivery_writer_execute.sql +51 -0
  12. package/migrations/20261104190000_agent_board_message_replay_scope.sql +373 -0
  13. package/package.json +1 -1
  14. package/schema-manifest.json +93 -3
  15. package/src/deployment-drift-main.test.mjs +156 -0
  16. package/src/deployment-drift-script.test.mjs +14 -1
  17. package/src/policy-comment.test.mjs +17 -0
  18. package/src/tooling.test.mjs +12 -2
  19. package/tests/agent_board_661_reply_delivery.test.sql +185 -0
  20. package/tests/agent_board_667_boundary.test.sql +162 -0
  21. package/tests/agent_board_667_conversion_branches.test.sql +91 -0
  22. package/tests/agent_board_667_conversion_profile.test.sql +112 -0
  23. package/tests/agent_board_667_history_class.test.sql +785 -0
  24. package/tests/agent_board_667_native_cast_effect.test.sql +88 -0
  25. package/tests/agent_board_667_realtime_apply_behavior.test.sql +117 -0
  26. package/tests/agent_board_667_realtime_apply_controls.test.sql +143 -0
  27. package/tests/agent_board_667_realtime_apply_profile.test.sql +61 -0
  28. package/tests/agent_board_667_retention.test.sql +201 -0
  29. package/tests/agent_board_667_routing.test.sql +294 -0
  30. package/tests/agent_board_667_sender_effects.test.sql +100 -0
  31. package/tests/agent_board_667_sender_profile_controls.test.sql +29 -0
  32. package/tests/agent_board_667_storage_delimiter_behavior.test.sql +84 -0
  33. package/tests/agent_board_667_storage_delimiter_controls.test.sql +23 -0
  34. package/tests/agent_board_667_storage_delimiter_profile.test.sql +61 -0
  35. package/tests/agent_board_667_storage_search_behavior.test.sql +93 -0
  36. package/tests/agent_board_667_storage_search_controls.test.sql +22 -0
  37. package/tests/agent_board_667_storage_search_profile.test.sql +60 -0
  38. package/tests/agent_board_667_storage_timestamp_behavior.test.sql +58 -0
  39. package/tests/agent_board_667_storage_timestamp_controls.test.sql +22 -0
  40. package/tests/agent_board_667_storage_timestamp_profile.test.sql +54 -0
  41. package/tests/agent_board_667_writers.test.sql +281 -0
  42. package/tests/agent_board_717_message_replay_scope.test.sql +828 -0
  43. package/tests/agent_board_717_tasking_replay_scope.test.sql +661 -0
  44. package/tests/agent_board_addressing.test.sql +8 -5
  45. package/tests/agent_board_claim_subject_gate.test.sql +7 -3
  46. package/tests/agent_board_claimpolicy_race.test.sql +39 -11
  47. package/tests/agent_board_consent_gating.test.sql +4 -4
  48. package/tests/agent_board_delegate_role_class.test.sql +33 -15
  49. package/tests/agent_board_delivery_writer_execute.test.sql +371 -0
  50. package/tests/agent_board_fk_lock_edges.test.sql +3 -0
  51. package/tests/agent_board_gatenull_membstate.test.sql +7 -4
  52. package/tests/agent_board_hosted_delegates.test.sql +5 -2
  53. package/tests/agent_board_inbox_rpcs.test.sql +5 -3
  54. package/tests/agent_board_obsgate.test.sql +8 -4
  55. package/tests/agent_board_obsgate4.test.sql +2 -2
  56. package/tests/agent_board_outbox_wake_ordering.test.sql +25 -2
  57. package/tests/agent_board_own_post_not_delivered.test.sql +11 -9
  58. package/tests/agent_board_policy_correlation.test.sql +189 -0
  59. package/tests/agent_board_request_removed.test.sql +96 -0
  60. package/tests/agent_board_resolution_authority.test.sql +190 -0
  61. package/tests/agent_board_session_identity_behavior_classes.test.sql +2 -2
  62. package/tests/agent_board_session_identity_limits.test.sql +1 -1
  63. package/tests/agent_board_session_identity_measured_mint.test.sql +342 -0
  64. package/tests/agent_board_session_identity_predicate_isolation.test.sql +146 -0
  65. package/tests/agent_board_session_provenance.test.sql +106 -0
  66. package/tests/agent_board_sidserver_authority.test.sql +69 -0
  67. package/tests/agent_board_sidserver_catalog.test.sql +47 -0
  68. package/tests/agent_board_signed_write_gateway.test.sql +4 -2
  69. package/tests/agent_board_task_thread_lock_order.test.sql +67 -42
  70. package/tests/agent_board_v2_invariants.test.sql +5 -2
  71. package/tests/agent_board_write_enforcement_class.test.sql +3 -3
  72. package/tests/catalog/function_execute_privileges.json +43 -7
  73. package/tests/catalog/p_metadata_parameters.json +16 -0
  74. package/tests/fixtures/agent_board_667_apply_rls_guard.sql +195 -0
  75. package/tests/fixtures/agent_board_667_apply_rls_provenance.md +125 -0
  76. package/tests/fixtures/agent_board_667_apply_rls_reference.sql +1903 -0
  77. package/tests/fixtures/agent_board_667_conversion_guard.sql +162 -0
  78. package/tests/fixtures/agent_board_667_conversion_reference.md +23 -0
  79. package/tests/fixtures/agent_board_667_conversion_reference.sql +5325 -0
  80. package/tests/fixtures/agent_board_667_delimiter_guard.sql +92 -0
  81. package/tests/fixtures/agent_board_667_delimiter_provenance.md +116 -0
  82. package/tests/fixtures/agent_board_667_delimiter_reference.sql +10150 -0
  83. package/tests/fixtures/agent_board_667_search_guard.sql +92 -0
  84. package/tests/fixtures/agent_board_667_search_provenance.md +141 -0
  85. package/tests/fixtures/agent_board_667_search_reference.sql +4779 -0
  86. package/tests/fixtures/agent_board_667_sender_guard.sql +102 -0
  87. package/tests/fixtures/agent_board_667_sender_reference.md +20 -0
  88. package/tests/fixtures/agent_board_667_sender_reference.sql +875 -0
  89. package/tests/fixtures/agent_board_667_timestamp_graph.sql +84 -0
  90. package/tests/fixtures/agent_board_667_timestamp_guard.sql +94 -0
  91. package/tests/fixtures/agent_board_667_timestamp_provenance.md +99 -0
  92. package/tests/fixtures/agent_board_667_timestamp_reference.sql +2938 -0
  93. package/tests/isolation/expected/association_artifact_anchor_order.out +108 -0
  94. package/tests/isolation/expected/association_board_vs_board.out +71 -0
  95. package/tests/isolation/expected/association_post_wait_authority.out +77 -0
  96. package/tests/isolation/expected/association_strong_history_writers.out +203 -0
  97. package/tests/isolation/expected/association_strong_isolation.out +185 -0
  98. package/tests/isolation/expected/association_vs_artifact_claim_escalation.out +199 -0
  99. package/tests/isolation/expected/association_vs_heartbeat.out +78 -0
  100. package/tests/isolation/expected/association_vs_multi_anchor_artifact.out +67 -0
  101. package/tests/isolation/expected/association_vs_private_message.out +67 -0
  102. package/tests/isolation/expected/association_vs_task_and_conflict.out +133 -0
  103. package/tests/isolation/expected/association_vs_taskful_cross_thread.out +133 -0
  104. package/tests/isolation/expected/consent_approval_lock.out +1 -1
  105. package/tests/isolation/expected/message_replay_scope.out +209 -0
  106. package/tests/isolation/expected/tasking_replay_scope.out +125 -0
  107. package/tests/isolation/specs/association_artifact_anchor_order.spec +46 -0
  108. package/tests/isolation/specs/association_board_vs_board.spec +37 -0
  109. package/tests/isolation/specs/association_post_wait_authority.spec +48 -0
  110. package/tests/isolation/specs/association_strong_history_writers.spec +61 -0
  111. package/tests/isolation/specs/association_strong_isolation.spec +50 -0
  112. package/tests/isolation/specs/association_vs_artifact_claim_escalation.spec +41 -0
  113. package/tests/isolation/specs/association_vs_heartbeat.spec +47 -0
  114. package/tests/isolation/specs/association_vs_multi_anchor_artifact.spec +38 -0
  115. package/tests/isolation/specs/association_vs_private_message.spec +33 -0
  116. package/tests/isolation/specs/association_vs_task_and_conflict.spec +38 -0
  117. package/tests/isolation/specs/association_vs_taskful_cross_thread.spec +42 -0
  118. package/tests/isolation/specs/consent_approval_lock.spec +13 -7
  119. package/tests/isolation/specs/message_replay_scope.spec +77 -0
  120. package/tests/isolation/specs/tasking_replay_scope.spec +72 -0
  121. package/tests/mutations/717-f1/M0_prefix_main_acl_and_comments.sql +11 -0
  122. package/tests/mutations/717-f1/M10_wrapper_stops_delegating.sql +7 -0
  123. package/tests/mutations/717-f1/M11_transitive_recipient_content.sql +8 -0
  124. package/tests/mutations/717-f1/M12_transitive_recipient_visibility.sql +8 -0
  125. package/tests/mutations/717-f1/M13_transitive_worker_content.sql +15 -0
  126. package/tests/mutations/717-f1/M1a_regrant_upsert_delivery_recipient.sql +1 -0
  127. package/tests/mutations/717-f1/M1b_regrant_upsert_inbox_item.sql +1 -0
  128. package/tests/mutations/717-f1/M1c_regrant_queue_inbox_callbacks.sql +1 -0
  129. package/tests/mutations/717-f1/M1d_regrant_queue_board_post_callbacks.sql +1 -0
  130. package/tests/mutations/717-f1/M1e_regrant_next_thread_message_sequence.sql +1 -0
  131. package/tests/mutations/717-f1/M2_blanket_schema_grant.sql +1 -0
  132. package/tests/mutations/717-f1/M3_new_overload_of_sink.sql +10 -0
  133. package/tests/mutations/717-f1/M4_new_route_calling_sink.sql +7 -0
  134. package/tests/mutations/717-f1/M5_actor_trusts_supplied_id.sql +5 -0
  135. package/tests/mutations/717-f1/M6_signed_route_granted_to_authenticated.sql +1 -0
  136. package/tests/mutations/717-f1/M7_worker_granted_to_authenticated.sql +1 -0
  137. package/tests/mutations/717-f1/M8_recipient_state_rewrites_visibility.sql +5 -0
  138. package/tests/mutations/717-f1/M9_public_route_revoked_from_service_role.sql +1 -0
  139. package/tests/mutations/717-f1/outbox_wake_ordering/W1_regrant_queue_inbox_callbacks.sql +1 -0
  140. package/tests/mutations/717-f1/outbox_wake_ordering/W2_revoke_worker_from_service_role.sql +1 -0
  141. package/tests/mutations/717-f1/outbox_wake_ordering/W3_invoker_caller_of_sink.sql +1 -0
  142. package/tests/mutations/717-f1/outbox_wake_ordering/W4_public_regrant_board_post_sink.sql +1 -0
  143. package/tests/mutations/717-f1/run.sh +17 -0
  144. package/tests/mutations/717-f2b/C1_new_delivery_route.sql +4 -0
  145. package/tests/mutations/717-f2b/C2_new_replay_lookup.sql +3 -0
  146. package/tests/mutations/717-f2b/C3_internal_regranted.sql +2 -0
  147. package/tests/mutations/717-f2b/C4_wrapper_writes_delivery.sql +9 -0
  148. package/tests/mutations/717-f2b/C5_class_member_dynamic_sql.sql +9 -0
  149. package/tests/mutations/717-f2b/C6_drop_board_scope_constraint.sql +2 -0
  150. package/tests/mutations/717-f2b/C7_state_private_helper_content.sql +14 -0
  151. package/tests/mutations/717-f2b/L1_dm_lock_split_by_recipient.sql +8 -0
  152. package/tests/mutations/717-f2b/L2_dm_lock_removed.sql +12 -0
  153. package/tests/mutations/717-f2b/L3_mc_lock_split_by_recipients.sql +8 -0
  154. package/tests/mutations/717-f2b/L4_mc_lock_removed.sql +12 -0
  155. package/tests/mutations/717-f2b/M0_prefix_definitions.sql +17 -0
  156. package/tests/mutations/717-f2b/M10_mc_lookup_any_author.sql +15 -0
  157. package/tests/mutations/717-f2b/M11_dm_swallow_refusal.sql +8 -0
  158. package/tests/mutations/717-f2b/M12_mc_swallow_refusal.sql +8 -0
  159. package/tests/mutations/717-f2b/M13_signed_dm_swallows_refusal.sql +32 -0
  160. package/tests/mutations/717-f2b/M14_signed_mc_swallows_refusal.sql +20 -0
  161. package/tests/mutations/717-f2b/M15_mc_any_source.sql +7 -0
  162. package/tests/mutations/717-f2b/M1_dm_drop_recipient_proof.sql +16 -0
  163. package/tests/mutations/717-f2b/M2_mc_drop_recipient_proof.sql +16 -0
  164. package/tests/mutations/717-f2b/M3_dm_metadata_as_proof.sql +12 -0
  165. package/tests/mutations/717-f2b/M4_mc_metadata_as_proof.sql +16 -0
  166. package/tests/mutations/717-f2b/M5_dm_any_mode.sql +11 -0
  167. package/tests/mutations/717-f2b/M6_dm_board_delivery_counts.sql +11 -0
  168. package/tests/mutations/717-f2b/M7_mc_any_mode.sql +11 -0
  169. package/tests/mutations/717-f2b/M8_dm_thread_stamp_as_proof.sql +12 -0
  170. package/tests/mutations/717-f2b/M9_dm_lookup_any_author.sql +15 -0
  171. package/tests/mutations/717-f2b/run.sh +82 -0
@@ -0,0 +1,195 @@
1
+ -- Requires the reviewed conversion and timestamp helpers in this transaction.
2
+ -- This admits one exact apply_rls OID; it is not a Realtime exemption. The
3
+ -- prepared target is named by regclass output, which is unqualified whenever
4
+ -- the relation is visible, and PostgreSQL re-parses a prepared statement when
5
+ -- the active search path changes (for example "$user" or USAGE under the
6
+ -- subscriber role). Every relation sharing a relname with a possible target is
7
+ -- therefore in the closure, independent of any session search path.
8
+ \ir agent_board_667_apply_rls_reference.sql
9
+ create temp table xb_ar_rel(rel oid primary key,origin text not null) on commit drop;
10
+ create temp table xb_ar_fn(fn oid primary key,origin text not null) on commit drop;
11
+ create temp table xb_ar_parse(fn oid primary key,tree text,problem text) on commit drop;
12
+ create function pg_temp.xb_ar_wrapper() returns oid
13
+ language sql volatile set search_path=pg_catalog as $$
14
+ select to_regprocedure(identity)::oid from pg_temp.xb_ar_reference
15
+ where kind='wrapper' and identity='realtime.apply_rls(jsonb,integer)'
16
+ $$;
17
+ create function pg_temp.xb_ar_meta(p_fn oid) returns jsonb
18
+ language sql volatile set search_path=pg_catalog as $$
19
+ select case when prokind='a' then pg_temp.xb_st_aggregate(oid) else pg_temp.xb_cf_meta(oid) end from pg_proc where oid=p_fn
20
+ $$;
21
+ create function pg_temp.xb_ar_prerequisite_problems() returns table(identity text,problem text)
22
+ language sql volatile set search_path=pg_catalog as $$
23
+ select identity,problem from pg_temp.xb_cf_problems()
24
+ union all select identity,problem from pg_temp.xb_cf_wrapper_problems()
25
+ union all select r.identity,'apply_rls declaration or security drift' from pg_temp.xb_ar_reference r
26
+ where r.kind in('wrapper','constructor') and r.payload is distinct from pg_temp.xb_cf_meta(to_regprocedure(r.identity))
27
+ union all select 'realtime.apply_rls(jsonb,integer)','apply_rls source coverage drift'
28
+ where (select md5(prosrc) from pg_proc where oid=pg_temp.xb_ar_wrapper())
29
+ is distinct from (select payload->>'prosrc_md5' from pg_temp.xb_ar_reference where kind='coverage')
30
+ $$;
31
+ -- Executable and relation edges stored in a parser tree. pg_depend omits pinned
32
+ -- built-ins, so the stored OIDs are read as well as the dependency rows.
33
+ create function pg_temp.xb_ar_tree_fns(p_tree text) returns table(fn oid)
34
+ language sql volatile set search_path=pg_catalog as $$
35
+ select distinct m[2]::oid from regexp_matches(coalesce(p_tree,''),
36
+ ':(funcid|aggfnoid|winfnoid|opfuncid|elemfuncid|hashfuncid|negfuncid) ([0-9]+)','g') m where m[2]::oid<>0
37
+ $$;
38
+ create function pg_temp.xb_ar_tree_rels(p_tree text) returns table(rel oid)
39
+ language sql volatile set search_path=pg_catalog as $$
40
+ select distinct m[1]::oid from regexp_matches(coalesce(p_tree,''),':relid ([0-9]+)','g') m where m[1]::oid<>0
41
+ $$;
42
+ -- A SQL body is parsed by recreating it as a temporary SQL-standard body under
43
+ -- the function's own pinned search_path, so the catalog stores its parse tree.
44
+ create function pg_temp.xb_ar_parse_sql(p_fn oid) returns void
45
+ language plpgsql volatile as $$
46
+ declare f record; body text; path text; nm text := 'xb_ar_parsed_'||p_fn; prior text := current_setting('search_path');
47
+ begin
48
+ delete from pg_temp.xb_ar_parse where fn=p_fn;
49
+ select p.*,l.lanname into f from pg_proc p join pg_language l on l.oid=p.prolang where p.oid=p_fn;
50
+ -- Built-in entry points are admitted only as individually reviewed leaves.
51
+ if f.lanname in('internal','c') then return; end if;
52
+ if f.lanname<>'sql' then
53
+ insert into pg_temp.xb_ar_parse values(p_fn,null,'helper language '||f.lanname);return;
54
+ end if;
55
+ if f.prosqlbody is not null then
56
+ insert into pg_temp.xb_ar_parse values(p_fn,f.prosqlbody::text,null);return;
57
+ end if;
58
+ select substr(c,13) into path from unnest(f.proconfig) c where c like 'search_path=%';
59
+ execute format('drop function if exists pg_temp.%I(%s)',nm,pg_get_function_identity_arguments(p_fn));
60
+ body := regexp_replace(f.prosrc,'[[:space:];]+$','');
61
+ begin
62
+ if path is not null then perform set_config('search_path',path,true); end if;
63
+ execute format('create function pg_temp.%I(%s) returns %s language sql begin atomic %s; end',
64
+ nm,pg_get_function_arguments(p_fn),pg_get_function_result(p_fn),body);
65
+ perform set_config('search_path',prior,true);
66
+ insert into pg_temp.xb_ar_parse select p_fn,p.prosqlbody::text,null from pg_proc p
67
+ where p.proname=nm and p.pronamespace=pg_my_temp_schema();
68
+ exception when others then
69
+ perform set_config('search_path',prior,true);
70
+ insert into pg_temp.xb_ar_parse values(p_fn,null,'unparsed SQL body '||sqlstate);
71
+ end;
72
+ end $$;
73
+ create function pg_temp.xb_ar_unpinned_path(p_fn oid) returns boolean
74
+ language sql volatile set search_path=pg_catalog as $$
75
+ select not exists(select 1 from pg_proc p cross join unnest(p.proconfig) c where p.oid=p_fn and c like 'search_path=%')
76
+ $$;
77
+ -- Reviewed leaves are not expanded further than their support and dependency
78
+ -- edges; their complete declarations are pinned. Everything else is parsed.
79
+ create function pg_temp.xb_ar_leaf(p_fn oid) returns boolean
80
+ language sql volatile set search_path=pg_catalog as $$
81
+ select exists(select 1 from pg_temp.xb_cf_primitives where fn=p_fn)
82
+ or exists(select 1 from pg_temp.xb_ar_reference where kind in('primitive','aggregate') and to_regprocedure(identity)=p_fn)
83
+ or exists(select 1 from pg_temp.xb_cf_wrappers where to_regprocedure(identity)=p_fn)
84
+ $$;
85
+ create function pg_temp.xb_ar_compute() returns void
86
+ language plpgsql volatile as $$
87
+ declare before bigint; after bigint; f oid; wrapper oid := pg_temp.xb_ar_wrapper();
88
+ begin
89
+ truncate pg_temp.xb_ar_rel,pg_temp.xb_ar_fn,pg_temp.xb_ar_parse;
90
+ if wrapper is null then return; end if;
91
+ -- Static reads of apply_rls, by dependency and by every FROM/JOIN name.
92
+ insert into pg_temp.xb_ar_rel select d.oid,'static apply_rls relation'
93
+ from extensions.plpgsql_show_dependency_tb(wrapper::regprocedure,0::oid) d where d.type='RELATION' on conflict do nothing;
94
+ insert into pg_temp.xb_ar_rel select c.oid,'same name as a static read' from pg_class c
95
+ where c.relname in(select (regexp_match(m[1],'([a-z_][a-z_0-9]*)$'))[1] from pg_proc p
96
+ cross join lateral regexp_matches(p.prosrc,'\m(?:from|join)\s+([a-z_][a-z_0-9.]*)','gi') m where p.oid=wrapper)
97
+ on conflict do nothing;
98
+ -- Only a row-security target reaches PREPARE/EXECUTE. Any same-named relation
99
+ -- can be the resolved target after a search-path change under another role.
100
+ insert into pg_temp.xb_ar_rel select c.oid,'row-security target or same-name resolution' from pg_class c
101
+ where c.relname in(select relname from pg_class where relrowsecurity and relkind in('r','p')) on conflict do nothing;
102
+ insert into pg_temp.xb_ar_fn select d.oid,'static apply_rls callable'
103
+ from extensions.plpgsql_show_dependency_tb(wrapper::regprocedure,0::oid) d where d.type='FUNCTION' on conflict do nothing;
104
+ insert into pg_temp.xb_ar_fn select p.oid,'static apply_rls name '||m[1] from pg_proc target
105
+ cross join lateral regexp_matches(target.prosrc,'([a-z_][a-z_0-9]*)\s*\(','g') m join pg_proc p on p.proname=m[1]
106
+ where target.oid=wrapper on conflict do nothing;
107
+ -- Grammar forms that call a function without its name before a parenthesis.
108
+ insert into pg_temp.xb_ar_fn select p.oid,'static apply_rls syntax '||(r.payload->>'syntax')
109
+ from pg_temp.xb_ar_reference r join pg_proc p on p.proname=r.identity
110
+ where r.kind='syntax' and position(r.payload->>'syntax' in (select lower(prosrc) from pg_proc where oid=wrapper))>0
111
+ on conflict do nothing;
112
+ insert into pg_temp.xb_ar_fn select to_regprocedure(identity),'constructor' from pg_temp.xb_ar_reference
113
+ where kind='constructor' on conflict do nothing;
114
+ loop
115
+ select (select count(*) from pg_temp.xb_ar_rel)+(select count(*) from pg_temp.xb_ar_fn) into before;
116
+ insert into pg_temp.xb_ar_rel select i.inhrelid,'inheritance child of '||r.rel::regclass
117
+ from pg_temp.xb_ar_rel r join pg_inherits i on i.inhparent=r.rel on conflict do nothing;
118
+ -- Every policy and rule on a closure relation, whatever its command.
119
+ insert into pg_temp.xb_ar_fn select x.fn,'policy '||p.polname||' on '||p.polrelid::regclass
120
+ from pg_policy p join pg_temp.xb_ar_rel r on r.rel=p.polrelid
121
+ cross join lateral (select fn from pg_temp.xb_ar_tree_fns(p.polqual::text||' '||coalesce(p.polwithcheck::text,''))
122
+ union select refobjid from pg_depend where classid='pg_policy'::regclass and objid=p.oid and refclassid='pg_proc'::regclass) x
123
+ on conflict do nothing;
124
+ insert into pg_temp.xb_ar_rel select x.rel,'policy '||p.polname||' on '||p.polrelid::regclass
125
+ from pg_policy p join pg_temp.xb_ar_rel r on r.rel=p.polrelid
126
+ cross join lateral (select rel from pg_temp.xb_ar_tree_rels(p.polqual::text||' '||coalesce(p.polwithcheck::text,''))
127
+ union select refobjid from pg_depend where classid='pg_policy'::regclass and objid=p.oid and refclassid='pg_class'::regclass) x
128
+ on conflict do nothing;
129
+ insert into pg_temp.xb_ar_fn select x.fn,'rule '||w.rulename||' on '||w.ev_class::regclass
130
+ from pg_rewrite w join pg_temp.xb_ar_rel r on r.rel=w.ev_class
131
+ cross join lateral (select fn from pg_temp.xb_ar_tree_fns(w.ev_action::text||' '||coalesce(w.ev_qual::text,''))
132
+ union select refobjid from pg_depend where classid='pg_rewrite'::regclass and objid=w.oid and refclassid='pg_proc'::regclass) x
133
+ on conflict do nothing;
134
+ insert into pg_temp.xb_ar_rel select x.rel,'rule '||w.rulename||' on '||w.ev_class::regclass
135
+ from pg_rewrite w join pg_temp.xb_ar_rel r on r.rel=w.ev_class
136
+ cross join lateral (select rel from pg_temp.xb_ar_tree_rels(w.ev_action::text||' '||coalesce(w.ev_qual::text,''))
137
+ union select refobjid from pg_depend where classid='pg_rewrite'::regclass and objid=w.oid and refclassid='pg_class'::regclass) x
138
+ where x.rel<>w.ev_class on conflict do nothing;
139
+ insert into pg_temp.xb_ar_fn select e.target,'support or dependency of '||pg_temp.xb_cf_fn(e.source) from (
140
+ select p.oid source,p.prosupport::oid target from pg_proc p join pg_temp.xb_ar_fn a on a.fn=p.oid where p.prosupport<>0
141
+ union select d.objid,d.refobjid from pg_depend d join pg_temp.xb_ar_fn a on a.fn=d.objid
142
+ where d.classid='pg_proc'::regclass and d.refclassid='pg_proc'::regclass
143
+ union select s.aggregate,s.fn from pg_temp.xb_st_aggregate_slots() s join pg_temp.xb_ar_fn a on a.fn=s.aggregate) e
144
+ on conflict do nothing;
145
+ for f in select a.fn from pg_temp.xb_ar_fn a where not pg_temp.xb_ar_leaf(a.fn)
146
+ and not exists(select 1 from pg_temp.xb_ar_parse p where p.fn=a.fn) loop
147
+ perform pg_temp.xb_ar_parse_sql(f);
148
+ end loop;
149
+ insert into pg_temp.xb_ar_fn select x.fn,'body of '||pg_temp.xb_cf_fn(p.fn) from pg_temp.xb_ar_parse p
150
+ cross join lateral pg_temp.xb_ar_tree_fns(p.tree) x on conflict do nothing;
151
+ insert into pg_temp.xb_ar_rel select x.rel,'body of '||pg_temp.xb_cf_fn(p.fn) from pg_temp.xb_ar_parse p
152
+ cross join lateral pg_temp.xb_ar_tree_rels(p.tree) x on conflict do nothing;
153
+ -- A body without a pinned search_path resolves names at run time.
154
+ insert into pg_temp.xb_ar_fn select o.oid,'same name as a run-time resolved call in '||pg_temp.xb_cf_fn(p.fn)
155
+ from pg_temp.xb_ar_parse p cross join lateral pg_temp.xb_ar_tree_fns(p.tree) x
156
+ join pg_proc c on c.oid=x.fn join pg_proc o on o.proname=c.proname
157
+ where pg_temp.xb_ar_unpinned_path(p.fn) on conflict do nothing;
158
+ insert into pg_temp.xb_ar_rel select o.oid,'same name as a run-time resolved read in '||pg_temp.xb_cf_fn(p.fn)
159
+ from pg_temp.xb_ar_parse p cross join lateral pg_temp.xb_ar_tree_rels(p.tree) x
160
+ join pg_class c on c.oid=x.rel join pg_class o on o.relname=c.relname
161
+ where pg_temp.xb_ar_unpinned_path(p.fn) on conflict do nothing;
162
+ select (select count(*) from pg_temp.xb_ar_rel)+(select count(*) from pg_temp.xb_ar_fn) into after;
163
+ exit when after=before;
164
+ end loop;
165
+ end $$;
166
+ create function pg_temp.xb_ar_problems() returns table(identity text,problem text)
167
+ language plpgsql volatile as $$
168
+ begin
169
+ return query select p.identity,p.problem from pg_temp.xb_ar_prerequisite_problems() p;
170
+ if pg_temp.xb_ar_wrapper() is null then
171
+ return query select 'realtime.apply_rls(jsonb,integer)'::text,'native apply_rls absent'::text;return;
172
+ end if;
173
+ perform pg_temp.xb_ar_compute();
174
+ -- Built-ins must be reviewed leaves with exact declarations. A SQL body is
175
+ -- instead admitted by its own parsed structure below, not by a frozen hash.
176
+ return query select pg_temp.xb_cf_fn(a.fn),'unreviewed apply_rls executable'::text from pg_temp.xb_ar_fn a
177
+ where not exists(select 1 from pg_temp.xb_cf_primitives c where c.fn=a.fn and c.metadata=pg_temp.xb_cf_meta(a.fn))
178
+ and not exists(select 1 from pg_temp.xb_cf_wrappers w where to_regprocedure(w.identity)=a.fn and w.metadata=pg_temp.xb_cf_meta(a.fn))
179
+ and not exists(select 1 from pg_temp.xb_ar_reference r where r.kind in('primitive','aggregate','constructor')
180
+ and to_regprocedure(r.identity)=a.fn and r.payload=pg_temp.xb_ar_meta(a.fn))
181
+ and not exists(select 1 from pg_temp.xb_ar_parse p where p.fn=a.fn and p.tree is not null and p.problem is null);
182
+ -- Every Query node of a parsed body, including CTEs, sublinks and CALL, must
183
+ -- be a SELECT. Its calls are in the closure above, so a writer anywhere below
184
+ -- names itself.
185
+ return query select pg_temp.xb_cf_fn(p.fn),coalesce(p.problem,'apply_rls helper is not SELECT-only') from pg_temp.xb_ar_parse p
186
+ where p.problem is not null or p.tree is null
187
+ or exists(select 1 from regexp_matches(p.tree,':commandType ([0-9]+)','g') m where m[1]<>'1');
188
+ return query select r.rel::regclass::text,'unreviewed apply_rls relation kind '||c.relkind::text from pg_temp.xb_ar_rel r
189
+ join pg_class c on c.oid=r.rel where c.relkind not in('r','p','v');
190
+ end $$;
191
+ create function pg_temp.xb_ar_admitted() returns table(fn oid)
192
+ language sql volatile set search_path=pg_catalog as $$
193
+ select pg_temp.xb_ar_wrapper() where pg_temp.xb_ar_wrapper() is not null
194
+ and not exists(select 1 from pg_temp.xb_ar_problems())
195
+ $$;
@@ -0,0 +1,125 @@
1
+ # Finite apply_rls effect disposition
2
+
3
+ This proof covers only `realtime.apply_rls(jsonb,integer)`. It is separate from
4
+ the conversion, sender and Storage reader proofs, which it composes. A missing
5
+ native function gives no native behavior or effect credit.
6
+
7
+ The reference input is the retained full Supabase CI catalog for `267dfa1d`,
8
+ SHA-256 `2c4598bd8b03474d54dc2221e17a83c6a47e1c14b48bd4fe2946e51d08a8160f`.
9
+ The captured `apply_rls` declaration has MD5 `d7b17bcce71440ab486770b53d15bd3d`
10
+ and is `SECURITY INVOKER` with no `SET` clause; `PUBLIC` holds `EXECUTE`. An
11
+ owned PostgreSQL 17.11.0.004 reconstruction reproduced that declaration, both SQL
12
+ constructors and every reached built-in declaration byte-for-byte. Declaration
13
+ equality is not C binary attestation.
14
+
15
+ ## Execution sites and the prepared target
16
+
17
+ The body has two `EXECUTE` sites. `build_prepared_statement_sql` constructs
18
+ `PREPARE walrus_rls_stmt AS SELECT EXISTS(SELECT 1 FROM <entity> WHERE <col>=<lit> ...)`,
19
+ with `quote_ident` columns and `quote_nullable` values; the second site executes
20
+ that statement after `set_config('role', <subscriber role>, true)` and
21
+ `set_config('request.jwt.claims', ...)`. Both sites are reached only when the
22
+ entity has row security and the action is not `DELETE`; conversion and filter
23
+ helpers run for every entity. The behavior test observes both boundaries.
24
+ The comparison operator, literal input, casts and operator-family support stay
25
+ inside the existing conversion registry, which pins every registered dispatch.
26
+
27
+ `<entity>` is `regclass` output, which is unqualified whenever the relation is
28
+ visible on the caller's search path. PostgreSQL re-parses a prepared statement
29
+ when the active search path changes between uses, and under the subscriber role
30
+ both `"$user"` expansion and schema `USAGE` change it. This was established by
31
+ experiment before relying on any target set, in an owned database with the exact
32
+ native routines:
33
+
34
+ - a schema named after the subscriber role holding a same-named view whose
35
+ qualifier calls a writer: the writer ran (`"$user"` redirection);
36
+ - a first-path schema the subscriber cannot use, with a same-named view later in
37
+ the path: the writer ran (`USAGE` redirection);
38
+ - the same entity with a schema-qualified `regclass` text: no write.
39
+
40
+ The native behavior test repeats the first case and its qualified control. So a
41
+ row-security target set alone is not a closure. The guard adds every relation in
42
+ any schema whose relname equals that of a row-security relation, which bounds
43
+ every resolution independent of search path, role or schema privileges.
44
+
45
+ ## Closure
46
+
47
+ Relations, to a fixed point: row-security targets and every same-named relation;
48
+ `apply_rls` static reads by `plpgsql_check` dependency and by every `FROM`/`JOIN`
49
+ name in the exact body, with their same-named relations; inheritance and partition
50
+ children; every relation referenced by a policy or rule; and every relation read
51
+ by a parsed SQL body, with its same-named relations when that body has no pinned
52
+ `search_path`. Only ordinary and partitioned tables and views are admitted kinds.
53
+
54
+ Executables: every routine named before a parenthesis in the exact `apply_rls`
55
+ body (all overloads, including aggregates), the two grammar forms that call a
56
+ function without that spelling (`trim(` and `AT TIME ZONE`), `plpgsql_check`
57
+ dependencies and both constructors; every policy, whatever its command, and every
58
+ rule on a closure relation, by `pg_depend` and by stored parser-tree OIDs; support,
59
+ dependency and aggregate-callback edges; and every SQL body reached.
60
+
61
+ A SQL body is parsed by recreating it as a temporary SQL-standard body under its
62
+ own pinned `search_path`, so PostgreSQL stores its parse tree. It is admitted only
63
+ if every `Query` node, including CTEs, sublinks and `CALL`, is a `SELECT`, and its
64
+ callees are themselves in the closure. A body without a pinned `search_path`
65
+ resolves names at run time, so every same-named routine and relation is added. SQL
66
+ bodies are admitted by this live structure rather than a frozen hash, so editing a
67
+ policy helper re-runs the proof instead of failing on drift. A body that does not
68
+ parse, or a helper in another procedural language, fails closed.
69
+
70
+ Built-ins are admitted only as reviewed leaves with exact declarations: the
71
+ existing conversion primitives, or this reference. The comparison dispatch helpers
72
+ `cast` and `check_equality_op` keep their existing conversion admission.
73
+
74
+ The over-approximations are deliberate. All policy commands and all rules are
75
+ included although a `SELECT` evaluates only `SELECT`/`ALL` policies and `ON SELECT`
76
+ rules, and the name scan reads comments, so a comment word before a parenthesis
77
+ adds `auth.role()`.
78
+
79
+ ## Reviewed built-in leaves
80
+
81
+ The reference holds the wrapper, source coverage, the two grammar forms, the two
82
+ pinned constructors, 106 primitive declarations and 16 aggregate dispatch records.
83
+ Four primitive declarations are inherited unchanged from the search, delimiter and
84
+ timestamp references. The generator binds every other one to its exact identity,
85
+ `internal` language and REL_17_11 entry point; it refuses catalog membership,
86
+ namespace or language as authority. The one SQL built-in,
87
+ `quote_nullable(anyelement)`, is bound to its exact body, which casts to `text`
88
+ through the argument type output and calls `quote_nullable(text)`.
89
+
90
+ All 105 internal entry points were located in the REL_17_11 sources (`acl.c`,
91
+ `guc_funcs.c`, `prepare.c`, `varlena.c`, `quote.c`, `oracle_compat.c`, `varbit.c`,
92
+ `varchar.c`, `formatting.c`, `timestamp.c`, `date.c`, `numeric.c`, `int8.c`,
93
+ `array_userfuncs.c`, `arrayfuncs.c`, `jsonb.c`, `jsonfuncs.c`, `tsvector_op.c`,
94
+ `multirangetypes.c`). None of their bodies calls SPI, `table_open` or a heap,
95
+ catalog, index or executor write entry point. The families are: GUC read/write (3),
96
+ catalog privilege lookup (12), the session prepared-statement hash (1), text
97
+ quoting, trimming, length and array rendering (10), datetime/numeric formatting and
98
+ time-zone arithmetic (17), set-returning expansion and its planner estimate (5),
99
+ jsonb construction (2) and aggregate entry and in-memory state functions (67).
100
+ Transitive C callees were not traced exhaustively. Dispatch through type I/O and
101
+ casts is bounded by the conversion registry. The `role` assign hook
102
+ (`variable.c`) checks `SET` membership against the session user; the behavior test
103
+ shows an unsettable subscriber role is refused with `42501`. `request.jwt.claims`
104
+ is not registered by any loaded library in the observed environment, but no
105
+ assertion pins that.
106
+
107
+ ## Recorded native behavior, not endorsed
108
+
109
+ After a successful call the role is reset to the session default rather than
110
+ restored to the entry role. The last subscriber's claims remain set for the rest
111
+ of the transaction, and `walrus_rls_stmt` remains a session resource. The behavior
112
+ test records all three. They change session state only; no closure routine writes
113
+ a relation, and the agent_board history relations are unchanged by the native
114
+ calls.
115
+
116
+ ## Limits
117
+
118
+ The guard is a catalog proof at test time. An object created later, including a
119
+ temporary relation in a caller's own session, is outside it until the next run;
120
+ session-local objects are the caller's own static capability. Nothing here attests
121
+ deployed binaries, extension hooks or arbitrary privileged SQL.
122
+
123
+ The manual generator accepts the retained native catalog and an owned closure
124
+ observation, writes only with explicit `--output`, and otherwise checks the
125
+ checked-in reference. There is no live production query or automatic refresh.