@consiliency/agent-board-schema 1.2.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.
- package/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +55 -0
- package/bin/agent-board-schema.js +97 -0
- package/fixtures/already-applied.json +181 -0
- package/fixtures/divergent-history.json +173 -0
- package/fixtures/production-history.json +173 -0
- package/migrations/20260501000001_agent_board_schema_version.sql +12 -0
- package/migrations/20260502000000_agent_board_schema.sql +180 -0
- package/migrations/20260503000000_agent_board_rpcs.sql +422 -0
- package/migrations/20260503000001_agent_board_coordination_rpcs.sql +229 -0
- package/migrations/20260504000000_agent_board_read_models.sql +206 -0
- package/migrations/20260505000000_agent_board_inbox_schema.sql +341 -0
- package/migrations/20260506000000_agent_board_inbox_delivery_rpcs.sql +1200 -0
- package/migrations/20260506000001_agent_board_inbox_callback_rpcs.sql +330 -0
- package/migrations/20260507000000_agent_board_inbox_reads.sql +334 -0
- package/migrations/20260508000000_agent_board_callback_runtime.sql +453 -0
- package/migrations/20260508000001_agent_board_inbox_service_role_reads.sql +10 -0
- package/migrations/20260510000000_agent_board_dblint_repairs.sql +413 -0
- package/migrations/20260511000000_agent_board_auth_schema.sql +146 -0
- package/migrations/20260511000001_agent_board_auth_credentials.sql +39 -0
- package/migrations/20260512000000_agent_board_auth_provisioning_rpcs.sql +622 -0
- package/migrations/20260512000001_agent_board_auth_v1_rpcs.sql +798 -0
- package/migrations/20260512000002_agent_board_auth_v2_rpcs.sql +1243 -0
- package/migrations/20260512000003_agent_board_auth_rls.sql +484 -0
- package/migrations/20260513000000_agent_board_backup_ops.sql +34 -0
- package/migrations/20260513000001_agent_board_retention.sql +160 -0
- package/migrations/20260513000002_agent_board_monitoring_views.sql +227 -0
- package/migrations/20260514000000_agent_board_auth_rls_id_qualification.sql +150 -0
- package/migrations/20260515000000_agent_board_boardopt.sql +493 -0
- package/migrations/20260516000000_agent_board_callbackcap.sql +113 -0
- package/migrations/20260517000000_agent_board_addressing.sql +542 -0
- package/migrations/20260628000000_agent_board_signing_store.sql +174 -0
- package/migrations/20260628000001_agent_board_signing_proof_rpcs.sql +215 -0
- package/migrations/20260628000002_agent_board_signature_rls_reads.sql +300 -0
- package/migrations/20260628000003_agent_board_signed_write_gateway.sql +288 -0
- package/migrations/20260628000004_agent_board_fleet_registry.sql +411 -0
- package/migrations/20260628000005_agent_board_fleet_policy.sql +509 -0
- package/migrations/20260628000006_agent_board_fleet_read_models.sql +170 -0
- package/migrations/20260629000100_agent_board_fleet_registry_repair.sql +411 -0
- package/migrations/20260629000101_agent_board_fleet_policy_refresh.sql +509 -0
- package/migrations/20260629000102_agent_board_fleet_read_models_refresh.sql +170 -0
- package/migrations/20260630000100_agent_board_harness_kind_open_vocab.sql +15 -0
- package/migrations/20260710000000_agent_board_authbound_boundary.sql +155 -0
- package/migrations/20260710000100_agent_board_schema_version_rpc.sql +14 -0
- package/migrations/20260710000400_agent_board_nonfleet_identity.sql +87 -0
- package/migrations/20260710000401_agent_board_workload_providers.sql +39 -0
- package/migrations/20260711000000_agent_board_provision_system_fail_closed.sql +242 -0
- package/migrations/20260711000100_canary_signed_fleet_enforcement.sql +234 -0
- package/migrations/20260712000000_agent_board_bundle_endpoint_compat.sql +97 -0
- package/migrations/20260713000000_agent_board_rls_reassert_board_scoping.sql +90 -0
- package/package.json +42 -0
- package/provenance.json +580 -0
- package/schema-manifest.json +458 -0
- package/src/check.js +23 -0
- package/src/index.js +50 -0
- package/src/materialize.js +73 -0
- package/src/plan.js +44 -0
- package/src/tooling.test.mjs +97 -0
- package/tests/agent_board_addressing.test.sql +166 -0
- package/tests/agent_board_auth_rls.test.sql +243 -0
- package/tests/agent_board_auth_rls_id_qualification.test.sql +225 -0
- package/tests/agent_board_auth_rpcs.test.sql +311 -0
- package/tests/agent_board_auth_schema.test.sql +411 -0
- package/tests/agent_board_authbound_boundary.test.sql +148 -0
- package/tests/agent_board_boardopt.test.sql +170 -0
- package/tests/agent_board_bundle_endpoint_compat.test.sql +49 -0
- package/tests/agent_board_callback_runtime.test.sql +425 -0
- package/tests/agent_board_callbackcap.test.sql +222 -0
- package/tests/agent_board_canary_signed_fleet_enforcement.test.sql +60 -0
- package/tests/agent_board_dblint_rpcs.test.sql +247 -0
- package/tests/agent_board_fleet_policy.test.sql +370 -0
- package/tests/agent_board_fleet_read_models.test.sql +158 -0
- package/tests/agent_board_fleet_registry.test.sql +149 -0
- package/tests/agent_board_inbox_reads.test.sql +471 -0
- package/tests/agent_board_inbox_rpcs.test.sql +452 -0
- package/tests/agent_board_inbox_schema.test.sql +355 -0
- package/tests/agent_board_nonfleet_identity.test.sql +26 -0
- package/tests/agent_board_rls_reassert_board_scoping.test.sql +39 -0
- package/tests/agent_board_schema_version.test.sql +24 -0
- package/tests/agent_board_schema_version_rpc.test.sql +40 -0
- package/tests/agent_board_signature_rls_reads.test.sql +133 -0
- package/tests/agent_board_signed_write_gateway.test.sql +219 -0
- package/tests/agent_board_signing_proof_rpcs.test.sql +143 -0
- package/tests/agent_board_signing_store.test.sql +183 -0
- package/tests/agent_board_smoke.test.sql +12 -0
- package/tests/agent_board_v1_invariants.test.sql +298 -0
- package/tests/agent_board_v2_invariants.test.sql +753 -0
- package/tests/agent_board_workload_providers.test.sql +15 -0
- package/tests/fixtures/adopter-base.sql +19 -0
- package/tests/fixtures/operator-hardening.sql +27 -0
- package/tests/fixtures/test-prereqs.sql +84 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
create or replace function agent_board.evaluate_signed_write_fleet_policy(
|
|
2
|
+
p_operation text,
|
|
3
|
+
p_payload jsonb,
|
|
4
|
+
p_gateway_decision jsonb
|
|
5
|
+
)
|
|
6
|
+
returns table(
|
|
7
|
+
allowed boolean,
|
|
8
|
+
actionable boolean,
|
|
9
|
+
denial_class text,
|
|
10
|
+
denied_recipient_agent_id uuid,
|
|
11
|
+
evaluated_at timestamptz
|
|
12
|
+
)
|
|
13
|
+
language plpgsql
|
|
14
|
+
security definer
|
|
15
|
+
set search_path = agent_board, public
|
|
16
|
+
as $$
|
|
17
|
+
declare
|
|
18
|
+
v_actor uuid := nullif(p_gateway_decision ->> 'agent_id', '')::uuid;
|
|
19
|
+
v_machine_id uuid;
|
|
20
|
+
v_board_id uuid;
|
|
21
|
+
v_recipient_ids uuid[] := '{}'::uuid[];
|
|
22
|
+
v_recipient_id uuid;
|
|
23
|
+
v_evidence_id uuid;
|
|
24
|
+
v_decision record;
|
|
25
|
+
v_status text := coalesce(nullif(p_gateway_decision ->> 'verification_status', ''), 'unsigned');
|
|
26
|
+
begin
|
|
27
|
+
if not agent_board._is_service_role() then
|
|
28
|
+
raise exception 'signed fleet policy evaluation requires service role' using errcode = '42501';
|
|
29
|
+
end if;
|
|
30
|
+
if v_actor is null then
|
|
31
|
+
raise exception 'actor_mismatch: signed write actor is required' using errcode = '42501';
|
|
32
|
+
end if;
|
|
33
|
+
|
|
34
|
+
select fma.machine_id
|
|
35
|
+
into v_machine_id
|
|
36
|
+
from agent_board.fleet_machine_agents fma
|
|
37
|
+
join agent_board.fleet_machines fm on fm.id = fma.machine_id
|
|
38
|
+
where fma.agent_id = v_actor
|
|
39
|
+
and fma.lifecycle_state = 'active'
|
|
40
|
+
and fm.lifecycle_state = 'active'
|
|
41
|
+
order by fma.updated_at desc
|
|
42
|
+
limit 1;
|
|
43
|
+
|
|
44
|
+
if v_machine_id is null then
|
|
45
|
+
return query select true, true, null::text, null::uuid, now();
|
|
46
|
+
return;
|
|
47
|
+
end if;
|
|
48
|
+
|
|
49
|
+
insert into agent_board.signature_evidence(
|
|
50
|
+
agent_id, signing_key_id, key_id, key_version, algorithm, source_kind,
|
|
51
|
+
operation, payload_digest, canonicalization_version, signature_digest,
|
|
52
|
+
verification_status, failure_class, signed_at, verified_at, nonce,
|
|
53
|
+
idempotency_key, external_evidence_ref, metadata
|
|
54
|
+
) values (
|
|
55
|
+
v_actor,
|
|
56
|
+
nullif(p_gateway_decision ->> 'signing_key_id', '')::uuid,
|
|
57
|
+
nullif(p_gateway_decision ->> 'key_id', ''),
|
|
58
|
+
nullif(p_gateway_decision ->> 'key_version', '')::integer,
|
|
59
|
+
coalesce(nullif(p_gateway_decision ->> 'algorithm', ''), 'Ed25519'),
|
|
60
|
+
'external',
|
|
61
|
+
p_operation,
|
|
62
|
+
coalesce(nullif(p_gateway_decision ->> 'payload_digest', ''), 'sha256:unsigned'),
|
|
63
|
+
coalesce(nullif(p_gateway_decision ->> 'canonicalization_version', ''), 'agent-provenance-jcs.v1'),
|
|
64
|
+
nullif(p_gateway_decision ->> 'signature_digest', ''),
|
|
65
|
+
case when v_status in ('unsigned', 'observed', 'warn', 'verified', 'failed') then v_status else 'failed' end,
|
|
66
|
+
nullif(p_gateway_decision ->> 'failure_class', ''),
|
|
67
|
+
nullif(p_gateway_decision ->> 'signed_at', '')::timestamptz,
|
|
68
|
+
nullif(p_gateway_decision ->> 'verified_at', '')::timestamptz,
|
|
69
|
+
nullif(p_gateway_decision ->> 'nonce', ''),
|
|
70
|
+
nullif(p_gateway_decision ->> 'idempotency_key', ''),
|
|
71
|
+
'signed-write-preflight:' || coalesce(nullif(p_gateway_decision ->> 'idempotency_key', ''), nullif(p_gateway_decision ->> 'nonce', ''), gen_random_uuid()::text),
|
|
72
|
+
jsonb_build_object('gateway', 'signed-write-preflight', 'redacted', true, 'metadata-safe', true)
|
|
73
|
+
) returning id into v_evidence_id;
|
|
74
|
+
|
|
75
|
+
if p_operation = 'send_direct_message' then
|
|
76
|
+
if nullif(p_payload ->> 'p_recipient_agent_id', '') is not null then
|
|
77
|
+
v_recipient_ids := array[(p_payload ->> 'p_recipient_agent_id')::uuid];
|
|
78
|
+
else
|
|
79
|
+
v_recipient_ids := agent_board._addressing_agent_ids_or_raise(v_actor, jsonb_build_array(p_payload -> 'p_recipient_descriptor'));
|
|
80
|
+
end if;
|
|
81
|
+
elsif p_operation = 'send_multicast_message' then
|
|
82
|
+
if p_payload ? 'p_recipient_agent_ids' then
|
|
83
|
+
select coalesce(array_agg(value::uuid), '{}'::uuid[]) into v_recipient_ids
|
|
84
|
+
from jsonb_array_elements_text(p_payload -> 'p_recipient_agent_ids');
|
|
85
|
+
else
|
|
86
|
+
v_recipient_ids := agent_board._addressing_agent_ids_or_raise(v_actor, p_payload -> 'p_recipient_descriptors');
|
|
87
|
+
end if;
|
|
88
|
+
elsif p_operation in ('post_board_message', 'create_tasking_delivery') then
|
|
89
|
+
if nullif(p_payload ->> 'p_board_id', '') is not null then
|
|
90
|
+
v_board_id := (p_payload ->> 'p_board_id')::uuid;
|
|
91
|
+
elsif nullif(p_payload ->> 'p_board_slug', '') is not null then
|
|
92
|
+
select id into v_board_id from agent_board.boards where slug = p_payload ->> 'p_board_slug' and status = 'active';
|
|
93
|
+
end if;
|
|
94
|
+
if p_operation = 'create_tasking_delivery' then
|
|
95
|
+
if p_payload ? 'p_recipient_agent_ids' then
|
|
96
|
+
select coalesce(array_agg(value::uuid), '{}'::uuid[]) into v_recipient_ids
|
|
97
|
+
from jsonb_array_elements_text(p_payload -> 'p_recipient_agent_ids');
|
|
98
|
+
elsif p_payload ? 'p_recipient_descriptors' and p_payload -> 'p_recipient_descriptors' <> 'null'::jsonb then
|
|
99
|
+
v_recipient_ids := agent_board._addressing_agent_ids_or_raise(v_actor, p_payload -> 'p_recipient_descriptors');
|
|
100
|
+
end if;
|
|
101
|
+
end if;
|
|
102
|
+
if v_board_id is not null then
|
|
103
|
+
select array(
|
|
104
|
+
select distinct recipient_id from (
|
|
105
|
+
select unnest(v_recipient_ids) as recipient_id
|
|
106
|
+
union
|
|
107
|
+
select bm.agent_id from agent_board.board_memberships bm
|
|
108
|
+
where bm.board_id = v_board_id and bm.membership_state in ('joined', 'subscribed')
|
|
109
|
+
) recipients
|
|
110
|
+
) into v_recipient_ids;
|
|
111
|
+
end if;
|
|
112
|
+
else
|
|
113
|
+
return query select true, true, null::text, null::uuid, now();
|
|
114
|
+
return;
|
|
115
|
+
end if;
|
|
116
|
+
|
|
117
|
+
if coalesce(cardinality(v_recipient_ids), 0) = 0 then
|
|
118
|
+
return query select false, false, 'recipient_policy_denied'::text, null::uuid, now();
|
|
119
|
+
return;
|
|
120
|
+
end if;
|
|
121
|
+
|
|
122
|
+
foreach v_recipient_id in array v_recipient_ids loop
|
|
123
|
+
select * into v_decision
|
|
124
|
+
from agent_board.evaluate_fleet_sender_policy(
|
|
125
|
+
v_actor,
|
|
126
|
+
v_recipient_id,
|
|
127
|
+
v_machine_id,
|
|
128
|
+
v_board_id,
|
|
129
|
+
p_operation,
|
|
130
|
+
v_evidence_id,
|
|
131
|
+
false
|
|
132
|
+
);
|
|
133
|
+
if not coalesce(v_decision.allowed, false) or not coalesce(v_decision.actionable, false) then
|
|
134
|
+
return query select false, false, coalesce(v_decision.denial_class, 'recipient_policy_denied'), v_recipient_id, now();
|
|
135
|
+
return;
|
|
136
|
+
end if;
|
|
137
|
+
end loop;
|
|
138
|
+
|
|
139
|
+
return query select true, true, null::text, null::uuid, now();
|
|
140
|
+
end;
|
|
141
|
+
$$;
|
|
142
|
+
|
|
143
|
+
create or replace function agent_board.signed_send_multicast_message(
|
|
144
|
+
p_payload jsonb,
|
|
145
|
+
p_gateway_decision jsonb
|
|
146
|
+
)
|
|
147
|
+
returns table(message_id uuid, delivered_recipient_count integer, inbox_item_count integer, callback_outbox_count integer, signature_evidence_id uuid, verification_status text, created_at timestamptz)
|
|
148
|
+
language plpgsql
|
|
149
|
+
security definer
|
|
150
|
+
set search_path = agent_board, public
|
|
151
|
+
as $$
|
|
152
|
+
declare
|
|
153
|
+
v_row record;
|
|
154
|
+
v_actor uuid := agent_board._signed_write_actor_id(p_payload, p_gateway_decision, 'p_author_agent_id');
|
|
155
|
+
v_evidence_id uuid;
|
|
156
|
+
v_recipient_ids uuid[];
|
|
157
|
+
begin
|
|
158
|
+
if p_payload ? 'p_recipient_agent_ids' then
|
|
159
|
+
select array_agg(value::uuid) into v_recipient_ids from jsonb_array_elements_text(p_payload -> 'p_recipient_agent_ids');
|
|
160
|
+
select * into v_row from agent_board.send_multicast_message(
|
|
161
|
+
(p_payload ->> 'p_thread_id')::uuid, v_actor, v_recipient_ids,
|
|
162
|
+
p_payload ->> 'p_subject', p_payload ->> 'p_body', coalesce(p_payload ->> 'p_message_type', 'comment'),
|
|
163
|
+
coalesce(p_payload ->> 'p_priority', 'medium'), nullif(p_payload ->> 'p_idempotency_key', ''),
|
|
164
|
+
coalesce(p_payload -> 'p_metadata', '{}'::jsonb) || jsonb_build_object('signed_write', jsonb_build_object('verification_status', coalesce(p_gateway_decision ->> 'verification_status', 'unsigned'), 'redacted', true))
|
|
165
|
+
);
|
|
166
|
+
else
|
|
167
|
+
select * into v_row from agent_board.send_multicast_message(
|
|
168
|
+
(p_payload ->> 'p_thread_id')::uuid, v_actor, p_payload -> 'p_recipient_descriptors',
|
|
169
|
+
coalesce(p_payload ->> 'p_addressing_mode', 'addressing_v1'), p_payload ->> 'p_subject', p_payload ->> 'p_body',
|
|
170
|
+
coalesce(p_payload ->> 'p_message_type', 'comment'), coalesce(p_payload ->> 'p_priority', 'medium'),
|
|
171
|
+
nullif(p_payload ->> 'p_idempotency_key', ''), coalesce(p_payload -> 'p_metadata', '{}'::jsonb)
|
|
172
|
+
);
|
|
173
|
+
end if;
|
|
174
|
+
v_evidence_id := agent_board._record_signed_write_evidence('message', v_row.message_id, 'send_multicast_message', p_payload, p_gateway_decision);
|
|
175
|
+
return query select v_row.message_id, v_row.delivered_recipient_count, v_row.inbox_item_count, v_row.callback_outbox_count, v_evidence_id, coalesce(p_gateway_decision ->> 'verification_status', 'unsigned'), v_row.created_at;
|
|
176
|
+
end;
|
|
177
|
+
$$;
|
|
178
|
+
|
|
179
|
+
create or replace function agent_board.signed_create_tasking_delivery(
|
|
180
|
+
p_payload jsonb,
|
|
181
|
+
p_gateway_decision jsonb
|
|
182
|
+
)
|
|
183
|
+
returns table(task_id uuid, created_event_id uuid, delivered_recipient_count integer, inbox_item_count integer, callback_outbox_count integer, signature_evidence_id uuid, verification_status text, created_at timestamptz, updated_at timestamptz)
|
|
184
|
+
language plpgsql
|
|
185
|
+
security definer
|
|
186
|
+
set search_path = agent_board, public
|
|
187
|
+
as $$
|
|
188
|
+
declare
|
|
189
|
+
v_row record;
|
|
190
|
+
v_actor uuid := agent_board._signed_write_actor_id(p_payload, p_gateway_decision, 'p_created_by_agent_id');
|
|
191
|
+
v_evidence_id uuid;
|
|
192
|
+
v_recipient_ids uuid[];
|
|
193
|
+
begin
|
|
194
|
+
if p_payload ? 'p_recipient_agent_ids' or p_payload ? 'p_board_id' then
|
|
195
|
+
if p_payload ? 'p_recipient_agent_ids' then
|
|
196
|
+
select array_agg(value::uuid) into v_recipient_ids from jsonb_array_elements_text(p_payload -> 'p_recipient_agent_ids');
|
|
197
|
+
end if;
|
|
198
|
+
select * into v_row from agent_board.create_tasking_delivery(
|
|
199
|
+
(p_payload ->> 'p_thread_id')::uuid, v_actor, p_payload ->> 'p_title', p_payload ->> 'p_details',
|
|
200
|
+
v_recipient_ids, nullif(p_payload ->> 'p_board_id', '')::uuid, coalesce(p_payload ->> 'p_priority', 'medium'),
|
|
201
|
+
coalesce(p_payload ->> 'p_orchestration_mode', 'distributed'), nullif(p_payload ->> 'p_idempotency_key', ''),
|
|
202
|
+
coalesce(p_payload -> 'p_metadata', '{}'::jsonb) || jsonb_build_object('signed_write', jsonb_build_object('verification_status', coalesce(p_gateway_decision ->> 'verification_status', 'unsigned'), 'redacted', true))
|
|
203
|
+
);
|
|
204
|
+
else
|
|
205
|
+
select * into v_row from agent_board.create_tasking_delivery(
|
|
206
|
+
(p_payload ->> 'p_thread_id')::uuid, v_actor, p_payload ->> 'p_title', p_payload ->> 'p_details',
|
|
207
|
+
p_payload -> 'p_recipient_descriptors', p_payload ->> 'p_board_slug', coalesce(p_payload ->> 'p_addressing_mode', 'addressing_v1'),
|
|
208
|
+
coalesce(p_payload ->> 'p_priority', 'medium'), coalesce(p_payload ->> 'p_orchestration_mode', 'distributed'),
|
|
209
|
+
nullif(p_payload ->> 'p_idempotency_key', ''), coalesce(p_payload -> 'p_metadata', '{}'::jsonb)
|
|
210
|
+
);
|
|
211
|
+
end if;
|
|
212
|
+
v_evidence_id := agent_board._record_signed_write_evidence('task_event', v_row.created_event_id, 'create_tasking_delivery', p_payload, p_gateway_decision);
|
|
213
|
+
return query select v_row.task_id, v_row.created_event_id, v_row.delivered_recipient_count, v_row.inbox_item_count, v_row.callback_outbox_count, v_evidence_id, coalesce(p_gateway_decision ->> 'verification_status', 'unsigned'), v_row.created_at, v_row.updated_at;
|
|
214
|
+
end;
|
|
215
|
+
$$;
|
|
216
|
+
|
|
217
|
+
revoke all on function agent_board.evaluate_signed_write_fleet_policy(text, jsonb, jsonb) from public, anon, authenticated;
|
|
218
|
+
revoke all on function agent_board.signed_send_multicast_message(jsonb, jsonb) from public, anon, authenticated;
|
|
219
|
+
revoke all on function agent_board.signed_create_tasking_delivery(jsonb, jsonb) from public, anon, authenticated;
|
|
220
|
+
grant execute on function agent_board.evaluate_signed_write_fleet_policy(text, jsonb, jsonb) to service_role;
|
|
221
|
+
grant execute on function agent_board.signed_send_multicast_message(jsonb, jsonb) to service_role;
|
|
222
|
+
grant execute on function agent_board.signed_create_tasking_delivery(jsonb, jsonb) to service_role;
|
|
223
|
+
|
|
224
|
+
revoke execute on function agent_board.send_direct_message(uuid, uuid, uuid, text, text, text, text, text, jsonb) from authenticated;
|
|
225
|
+
revoke execute on function agent_board.send_direct_message(uuid, uuid, jsonb, text, text, text, text, text, text, jsonb) from authenticated;
|
|
226
|
+
revoke execute on function agent_board.send_multicast_message(uuid, uuid, uuid[], text, text, text, text, text, jsonb) from authenticated;
|
|
227
|
+
revoke execute on function agent_board.send_multicast_message(uuid, uuid, jsonb, text, text, text, text, text, text, jsonb) from authenticated;
|
|
228
|
+
revoke execute on function agent_board.post_board_message(uuid, uuid, uuid, text, text, text, text, text, jsonb) from authenticated;
|
|
229
|
+
revoke execute on function agent_board.post_board_message(uuid, uuid, text, text, text, text, text, text, text, jsonb) from authenticated;
|
|
230
|
+
revoke execute on function agent_board.create_tasking_delivery(uuid, uuid, text, text, uuid[], uuid, text, text, text, jsonb) from authenticated;
|
|
231
|
+
revoke execute on function agent_board.create_tasking_delivery(uuid, uuid, text, text, jsonb, text, text, text, text, text, jsonb) from authenticated;
|
|
232
|
+
|
|
233
|
+
insert into agent_board.schema_version(version) values (5)
|
|
234
|
+
on conflict ((true)) do update set version = greatest(agent_board.schema_version.version, excluded.version);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
-- MBENDPOINT — bundle-installation metadata + authenticated compatibility RPC.
|
|
2
|
+
-- Message Board-owned FORWARD migration (IF-0-MBPLANE-ENDPOINT-ARTIFACT-1),
|
|
3
|
+
-- appended after the frozen union. Additive: it does NOT change the
|
|
4
|
+
-- client-facing agent_board.schema_version (7). The install record is written
|
|
5
|
+
-- by the materializer / adopter at apply time via record_bundle_installation();
|
|
6
|
+
-- it is never hardcoded to a not-yet-selected release here.
|
|
7
|
+
|
|
8
|
+
-- Singleton identity of the installed @consiliency/agent-board-schema bundle.
|
|
9
|
+
create table if not exists agent_board.bundle_installation (
|
|
10
|
+
id boolean primary key default true,
|
|
11
|
+
constraint bundle_installation_singleton check (id),
|
|
12
|
+
package_version text not null,
|
|
13
|
+
bundle_digest text not null,
|
|
14
|
+
schema_version integer not null,
|
|
15
|
+
source_commit text,
|
|
16
|
+
materializer_receipt text,
|
|
17
|
+
installed_at timestamptz not null default now()
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
comment on table agent_board.bundle_installation is
|
|
21
|
+
'Singleton: identity of the installed @consiliency/agent-board-schema release. Written by the materializer at apply time; consumed by get_bundle_compatibility().';
|
|
22
|
+
|
|
23
|
+
-- Written by the materializer/adopter at apply time to record the exact
|
|
24
|
+
-- installed bundle. Service-role only.
|
|
25
|
+
create or replace function agent_board.record_bundle_installation(
|
|
26
|
+
p_package_version text,
|
|
27
|
+
p_bundle_digest text,
|
|
28
|
+
p_schema_version integer,
|
|
29
|
+
p_source_commit text default null,
|
|
30
|
+
p_materializer_receipt text default null
|
|
31
|
+
)
|
|
32
|
+
returns void
|
|
33
|
+
language sql
|
|
34
|
+
security definer
|
|
35
|
+
set search_path = agent_board, pg_temp
|
|
36
|
+
as $$
|
|
37
|
+
insert into agent_board.bundle_installation as bi
|
|
38
|
+
(id, package_version, bundle_digest, schema_version, source_commit, materializer_receipt, installed_at)
|
|
39
|
+
values (true, p_package_version, p_bundle_digest, p_schema_version, p_source_commit, p_materializer_receipt, now())
|
|
40
|
+
on conflict (id) do update set
|
|
41
|
+
package_version = excluded.package_version,
|
|
42
|
+
bundle_digest = excluded.bundle_digest,
|
|
43
|
+
schema_version = excluded.schema_version,
|
|
44
|
+
source_commit = excluded.source_commit,
|
|
45
|
+
materializer_receipt = excluded.materializer_receipt,
|
|
46
|
+
installed_at = now();
|
|
47
|
+
$$;
|
|
48
|
+
|
|
49
|
+
revoke all on function agent_board.record_bundle_installation(text, text, integer, text, text) from public, anon, authenticated;
|
|
50
|
+
grant execute on function agent_board.record_bundle_installation(text, text, integer, text, text) to service_role;
|
|
51
|
+
|
|
52
|
+
-- Authenticated compatibility health. Reports schema version, installed bundle
|
|
53
|
+
-- identity, signed-write enforcement posture, and redacted readiness. Returns
|
|
54
|
+
-- NO credential, secret, or row content.
|
|
55
|
+
create or replace function agent_board.get_bundle_compatibility()
|
|
56
|
+
returns jsonb
|
|
57
|
+
language sql
|
|
58
|
+
stable
|
|
59
|
+
security definer
|
|
60
|
+
set search_path = agent_board, pg_temp
|
|
61
|
+
as $$
|
|
62
|
+
select jsonb_build_object(
|
|
63
|
+
'schema_version', (select version from agent_board.schema_version limit 1),
|
|
64
|
+
'installed_bundle_version', (select package_version from agent_board.bundle_installation limit 1),
|
|
65
|
+
'installed_bundle_digest', (select bundle_digest from agent_board.bundle_installation limit 1),
|
|
66
|
+
'installed_schema_version', (select schema_version from agent_board.bundle_installation limit 1),
|
|
67
|
+
'source_commit', (select source_commit from agent_board.bundle_installation limit 1),
|
|
68
|
+
-- Signed-write enforcement is active when the signed gateway exists AND
|
|
69
|
+
-- NONE of the four canary-revoked raw write RPCs are directly executable by
|
|
70
|
+
-- authenticated (writes must be channeled through the signed gateway). The
|
|
71
|
+
-- probe checks all four RPCs, not just one, so a partial re-grant on any of
|
|
72
|
+
-- them correctly reports enforcement as NOT fully active.
|
|
73
|
+
'signed_write_enforced', (
|
|
74
|
+
to_regprocedure('agent_board.signed_send_direct_message(jsonb, jsonb)') is not null
|
|
75
|
+
and not exists (
|
|
76
|
+
select 1 from information_schema.role_routine_grants
|
|
77
|
+
where routine_schema = 'agent_board'
|
|
78
|
+
and routine_name in (
|
|
79
|
+
'send_direct_message', 'send_multicast_message',
|
|
80
|
+
'post_board_message', 'create_tasking_delivery'
|
|
81
|
+
)
|
|
82
|
+
and grantee = 'authenticated'
|
|
83
|
+
and privilege_type = 'EXECUTE'
|
|
84
|
+
)
|
|
85
|
+
),
|
|
86
|
+
-- Ready only once the materializer has recorded an install AND the schema
|
|
87
|
+
-- version handshake resolves.
|
|
88
|
+
'ready', (
|
|
89
|
+
(select version from agent_board.schema_version limit 1) is not null
|
|
90
|
+
and exists (select 1 from agent_board.bundle_installation)
|
|
91
|
+
),
|
|
92
|
+
'checked_at', now()
|
|
93
|
+
);
|
|
94
|
+
$$;
|
|
95
|
+
|
|
96
|
+
revoke all on function agent_board.get_bundle_compatibility() from public, anon;
|
|
97
|
+
grant execute on function agent_board.get_bundle_compatibility() to authenticated, service_role;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
-- Forward corrective migration (rollback Class B) — reassert the board-scoping
|
|
2
|
+
-- SELECT policies for delivery_recipients, inbox_items, and tasks to their
|
|
3
|
+
-- canonical, board-scoped (qualified) form.
|
|
4
|
+
--
|
|
5
|
+
-- WHY: a production database can carry #208 drift bytes that predate/omit the
|
|
6
|
+
-- 20260514000000 board-scoping fix, leaving these three policies collapsed to
|
|
7
|
+
-- the LEAKING tautology (bm.board_id = bm.board_id / dr.source_task_id = dr.id)
|
|
8
|
+
-- while its migration history claims 20260514000000 is applied — so version-ID
|
|
9
|
+
-- recognition would skip re-running it. This new-ID migration reasserts the
|
|
10
|
+
-- correct predicates. Effect of the leak: an authenticated agent who is a
|
|
11
|
+
-- joined/subscribed member of ANY board could read delivery_recipients and
|
|
12
|
+
-- inbox_items rows scoped to ANY OTHER board.
|
|
13
|
+
--
|
|
14
|
+
-- SCOPE: exactly the three policies the normalized catalog-equivalence check
|
|
15
|
+
-- flagged as bundle-owned divergences (plans/MBPLANE-CATALOG-EQUIVALENCE-*).
|
|
16
|
+
-- The 20260514000000 `boards` and `messages` policies are intentionally NOT
|
|
17
|
+
-- reasserted here: `boards` was superseded by 20260515000000_agent_board_boardopt
|
|
18
|
+
-- (reasserting the older form would revert board discovery), and prod's
|
|
19
|
+
-- `messages` policy already matches the bundle. Idempotent; only TIGHTENS access
|
|
20
|
+
-- (leaky -> board-scoped); no schema-shape change; does NOT write schema_version.
|
|
21
|
+
|
|
22
|
+
drop policy if exists agent_board_authenticated_select_tasks on agent_board.tasks;
|
|
23
|
+
create policy agent_board_authenticated_select_tasks
|
|
24
|
+
on agent_board.tasks
|
|
25
|
+
as permissive
|
|
26
|
+
for select
|
|
27
|
+
to authenticated
|
|
28
|
+
using (
|
|
29
|
+
created_by_agent_id = agent_board.current_agent_id()
|
|
30
|
+
or assigned_agent_id = agent_board.current_agent_id()
|
|
31
|
+
or exists (
|
|
32
|
+
select 1
|
|
33
|
+
from agent_board.delivery_recipients dr
|
|
34
|
+
where dr.source_task_id = tasks.id
|
|
35
|
+
and (
|
|
36
|
+
dr.recipient_agent_id = agent_board.current_agent_id()
|
|
37
|
+
or (
|
|
38
|
+
dr.board_id is not null
|
|
39
|
+
and exists (
|
|
40
|
+
select 1
|
|
41
|
+
from agent_board.board_memberships bm
|
|
42
|
+
where bm.board_id = dr.board_id
|
|
43
|
+
and bm.agent_id = agent_board.current_agent_id()
|
|
44
|
+
and bm.membership_state in ('joined', 'subscribed')
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
drop policy if exists agent_board_authenticated_select_delivery_recipients on agent_board.delivery_recipients;
|
|
52
|
+
create policy agent_board_authenticated_select_delivery_recipients
|
|
53
|
+
on agent_board.delivery_recipients
|
|
54
|
+
as permissive
|
|
55
|
+
for select
|
|
56
|
+
to authenticated
|
|
57
|
+
using (
|
|
58
|
+
recipient_agent_id = agent_board.current_agent_id()
|
|
59
|
+
or (
|
|
60
|
+
board_id is not null
|
|
61
|
+
and exists (
|
|
62
|
+
select 1
|
|
63
|
+
from agent_board.board_memberships bm
|
|
64
|
+
where bm.board_id = delivery_recipients.board_id
|
|
65
|
+
and bm.agent_id = agent_board.current_agent_id()
|
|
66
|
+
and bm.membership_state in ('joined', 'subscribed')
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
drop policy if exists agent_board_authenticated_select_inbox_items on agent_board.inbox_items;
|
|
72
|
+
create policy agent_board_authenticated_select_inbox_items
|
|
73
|
+
on agent_board.inbox_items
|
|
74
|
+
as permissive
|
|
75
|
+
for select
|
|
76
|
+
to authenticated
|
|
77
|
+
using (
|
|
78
|
+
recipient_agent_id = agent_board.current_agent_id()
|
|
79
|
+
or (
|
|
80
|
+
board_id is not null
|
|
81
|
+
and visible_in_board_feed = true
|
|
82
|
+
and exists (
|
|
83
|
+
select 1
|
|
84
|
+
from agent_board.board_memberships bm
|
|
85
|
+
where bm.board_id = inbox_items.board_id
|
|
86
|
+
and bm.agent_id = agent_board.current_agent_id()
|
|
87
|
+
and bm.membership_state in ('joined', 'subscribed')
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
);
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@consiliency/agent-board-schema",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Canonical, immutable, exact-pinnable agent_board schema bundle: ordered SQL migrations, digest + compatibility manifest, and deterministic materialize/plan/check tooling for adopters.",
|
|
5
|
+
"repository": "https://github.com/Consiliency/message-board.git",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./src/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.js",
|
|
10
|
+
"./manifest": "./schema-manifest.json",
|
|
11
|
+
"./provenance": "./provenance.json"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"agent-board-schema": "./bin/agent-board-schema.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"bin",
|
|
18
|
+
"src",
|
|
19
|
+
"migrations",
|
|
20
|
+
"tests",
|
|
21
|
+
"fixtures",
|
|
22
|
+
"schema-manifest.json",
|
|
23
|
+
"provenance.json",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"NOTICE"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"registry": "https://registry.npmjs.org"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"license": "Apache-2.0",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build:manifest": "node ../../scripts/build-schema-manifest.mjs",
|
|
38
|
+
"check:manifest": "node ../../scripts/build-schema-manifest.mjs --check",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"pack:smoke": "node ../../scripts/schema-packed-consumer-smoke.mjs"
|
|
41
|
+
}
|
|
42
|
+
}
|