@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,355 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
|
|
3
|
+
select plan(24);
|
|
4
|
+
|
|
5
|
+
select has_table('agent_board', 'boards', 'boards table exists');
|
|
6
|
+
select has_table('agent_board', 'board_memberships', 'board_memberships table exists');
|
|
7
|
+
select has_table('agent_board', 'delivery_recipients', 'delivery_recipients table exists');
|
|
8
|
+
select has_table('agent_board', 'inbox_items', 'inbox_items table exists');
|
|
9
|
+
select has_table('agent_board', 'callback_endpoints', 'callback_endpoints table exists');
|
|
10
|
+
select has_table('agent_board', 'callback_subscriptions', 'callback_subscriptions table exists');
|
|
11
|
+
select has_table('agent_board', 'callback_outbox_events', 'callback_outbox_events table exists');
|
|
12
|
+
select has_table('agent_board', 'callback_delivery_attempts', 'callback_delivery_attempts table exists');
|
|
13
|
+
|
|
14
|
+
select has_column('agent_board', 'inbox_items', 'action_state', 'inbox_items stores compact action state');
|
|
15
|
+
select has_column('agent_board', 'inbox_items', 'fetch_descriptor', 'inbox_items stores canonical fetch descriptors');
|
|
16
|
+
select has_column('agent_board', 'callback_outbox_events', 'idempotency_key', 'callback outbox stores durable idempotency keys');
|
|
17
|
+
select has_column('agent_board', 'callback_delivery_attempts', 'next_retry_at', 'delivery attempts store retry schedule metadata');
|
|
18
|
+
|
|
19
|
+
select ok(
|
|
20
|
+
(
|
|
21
|
+
select relrowsecurity
|
|
22
|
+
from pg_class
|
|
23
|
+
where relnamespace = 'agent_board'::regnamespace
|
|
24
|
+
and relname = 'boards'
|
|
25
|
+
),
|
|
26
|
+
'boards has RLS enabled'
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
select ok(
|
|
30
|
+
(
|
|
31
|
+
select relrowsecurity
|
|
32
|
+
from pg_class
|
|
33
|
+
where relnamespace = 'agent_board'::regnamespace
|
|
34
|
+
and relname = 'inbox_items'
|
|
35
|
+
),
|
|
36
|
+
'inbox_items has RLS enabled'
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
select ok(
|
|
40
|
+
(
|
|
41
|
+
select relrowsecurity
|
|
42
|
+
from pg_class
|
|
43
|
+
where relnamespace = 'agent_board'::regnamespace
|
|
44
|
+
and relname = 'callback_outbox_events'
|
|
45
|
+
),
|
|
46
|
+
'callback_outbox_events has RLS enabled'
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
select ok(
|
|
50
|
+
(
|
|
51
|
+
select relrowsecurity
|
|
52
|
+
from pg_class
|
|
53
|
+
where relnamespace = 'agent_board'::regnamespace
|
|
54
|
+
and relname = 'callback_delivery_attempts'
|
|
55
|
+
),
|
|
56
|
+
'callback_delivery_attempts has RLS enabled'
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
select ok(
|
|
60
|
+
exists (
|
|
61
|
+
select 1
|
|
62
|
+
from pg_indexes
|
|
63
|
+
where schemaname = 'agent_board'
|
|
64
|
+
and tablename = 'board_memberships'
|
|
65
|
+
and indexname = 'idx_agent_board_board_memberships_active_unique'
|
|
66
|
+
),
|
|
67
|
+
'board_memberships enforces one active membership per board and agent'
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
select ok(
|
|
71
|
+
exists (
|
|
72
|
+
select 1
|
|
73
|
+
from pg_indexes
|
|
74
|
+
where schemaname = 'agent_board'
|
|
75
|
+
and tablename = 'delivery_recipients'
|
|
76
|
+
and indexname = 'idx_agent_board_delivery_recipients_unique_source_recipient'
|
|
77
|
+
),
|
|
78
|
+
'delivery_recipients suppresses duplicate source-plus-recipient rows'
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
select ok(
|
|
82
|
+
exists (
|
|
83
|
+
select 1
|
|
84
|
+
from pg_indexes
|
|
85
|
+
where schemaname = 'agent_board'
|
|
86
|
+
and tablename = 'inbox_items'
|
|
87
|
+
and indexname = 'idx_agent_board_inbox_items_agent_pending'
|
|
88
|
+
),
|
|
89
|
+
'inbox_items has a pending-action index for per-agent triage queries'
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
select ok(
|
|
93
|
+
exists (
|
|
94
|
+
select 1
|
|
95
|
+
from pg_indexes
|
|
96
|
+
where schemaname = 'agent_board'
|
|
97
|
+
and tablename = 'callback_outbox_events'
|
|
98
|
+
and indexname = 'idx_agent_board_callback_outbox_events_poll'
|
|
99
|
+
),
|
|
100
|
+
'callback_outbox_events has a polling index for pending and retryable rows'
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
select ok(
|
|
104
|
+
exists (
|
|
105
|
+
select 1
|
|
106
|
+
from pg_indexes
|
|
107
|
+
where schemaname = 'agent_board'
|
|
108
|
+
and tablename = 'callback_delivery_attempts'
|
|
109
|
+
and indexname = 'idx_agent_board_callback_delivery_attempts_retry_schedule'
|
|
110
|
+
and indexdef ilike '%attempt_outcome = ''retryable_failure''%'
|
|
111
|
+
),
|
|
112
|
+
'callback_delivery_attempts has a retry scheduling index'
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
select ok(
|
|
116
|
+
(
|
|
117
|
+
select count(*) = 8
|
|
118
|
+
from pg_policies
|
|
119
|
+
where schemaname = 'agent_board'
|
|
120
|
+
and policyname in (
|
|
121
|
+
'agent_board_service_all_boards',
|
|
122
|
+
'agent_board_service_all_board_memberships',
|
|
123
|
+
'agent_board_service_all_delivery_recipients',
|
|
124
|
+
'agent_board_service_all_inbox_items',
|
|
125
|
+
'agent_board_service_all_callback_endpoints',
|
|
126
|
+
'agent_board_service_all_callback_subscriptions',
|
|
127
|
+
'agent_board_service_all_callback_outbox_events',
|
|
128
|
+
'agent_board_service_all_callback_delivery_attempts'
|
|
129
|
+
)
|
|
130
|
+
),
|
|
131
|
+
'new inbox-schema tables inherit service_role-only write posture'
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
select ok(
|
|
135
|
+
(
|
|
136
|
+
select count(*) = 0
|
|
137
|
+
from pg_policies
|
|
138
|
+
where schemaname = 'agent_board'
|
|
139
|
+
and tablename in (
|
|
140
|
+
'boards',
|
|
141
|
+
'board_memberships',
|
|
142
|
+
'delivery_recipients',
|
|
143
|
+
'inbox_items',
|
|
144
|
+
'callback_endpoints',
|
|
145
|
+
'callback_subscriptions',
|
|
146
|
+
'callback_outbox_events',
|
|
147
|
+
'callback_delivery_attempts'
|
|
148
|
+
)
|
|
149
|
+
and cmd in ('INSERT', 'UPDATE', 'DELETE')
|
|
150
|
+
and roles && array['authenticated'::name]
|
|
151
|
+
),
|
|
152
|
+
'new inbox-schema tables expose no authenticated write policies'
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
do $$
|
|
156
|
+
declare
|
|
157
|
+
v_sender uuid := gen_random_uuid();
|
|
158
|
+
v_recipient uuid := gen_random_uuid();
|
|
159
|
+
v_board_member uuid := gen_random_uuid();
|
|
160
|
+
v_thread uuid := gen_random_uuid();
|
|
161
|
+
v_message uuid := gen_random_uuid();
|
|
162
|
+
v_board uuid;
|
|
163
|
+
v_membership uuid;
|
|
164
|
+
v_direct_recipient uuid;
|
|
165
|
+
v_board_recipient uuid;
|
|
166
|
+
v_inbox_item uuid;
|
|
167
|
+
v_endpoint uuid;
|
|
168
|
+
v_subscription uuid;
|
|
169
|
+
v_outbox uuid;
|
|
170
|
+
begin
|
|
171
|
+
insert into agent_board.agents(id, agent_type, handle, display_name)
|
|
172
|
+
values
|
|
173
|
+
(v_sender, 'agent', 'test_inboxschema_sender', 'Inbox Sender'),
|
|
174
|
+
(v_recipient, 'agent', 'test_inboxschema_recipient', 'Inbox Recipient'),
|
|
175
|
+
(v_board_member, 'agent', 'test_inboxschema_board_member', 'Board Member');
|
|
176
|
+
|
|
177
|
+
insert into agent_board.threads(id, thread_type, created_by_agent_id, subject)
|
|
178
|
+
values (v_thread, 'coordination', v_sender, 'Inbox schema test thread');
|
|
179
|
+
|
|
180
|
+
insert into agent_board.messages(id, thread_id, author_agent_id, message_sequence, message_type, body)
|
|
181
|
+
values (v_message, v_thread, v_sender, 1, 'comment', 'schema test message');
|
|
182
|
+
|
|
183
|
+
insert into agent_board.boards(slug, name, created_by_agent_id)
|
|
184
|
+
values ('test-inboxschema-board', 'Inbox Schema Test Board', v_sender)
|
|
185
|
+
returning id into v_board;
|
|
186
|
+
|
|
187
|
+
insert into agent_board.board_memberships(board_id, agent_id, membership_state, created_by_agent_id)
|
|
188
|
+
values (v_board, v_board_member, 'joined', v_sender)
|
|
189
|
+
returning id into v_membership;
|
|
190
|
+
|
|
191
|
+
insert into agent_board.delivery_recipients(delivery_mode, recipient_agent_id, source_kind, source_message_id)
|
|
192
|
+
values ('direct', v_recipient, 'message', v_message)
|
|
193
|
+
returning id into v_direct_recipient;
|
|
194
|
+
|
|
195
|
+
insert into agent_board.delivery_recipients(
|
|
196
|
+
delivery_mode,
|
|
197
|
+
recipient_agent_id,
|
|
198
|
+
source_kind,
|
|
199
|
+
source_message_id,
|
|
200
|
+
board_id,
|
|
201
|
+
board_membership_id
|
|
202
|
+
)
|
|
203
|
+
values ('board', v_board_member, 'message', v_message, v_board, v_membership)
|
|
204
|
+
returning id into v_board_recipient;
|
|
205
|
+
|
|
206
|
+
insert into agent_board.inbox_items(
|
|
207
|
+
delivery_recipient_id,
|
|
208
|
+
recipient_agent_id,
|
|
209
|
+
board_id,
|
|
210
|
+
delivery_mode,
|
|
211
|
+
source_kind,
|
|
212
|
+
source_message_id,
|
|
213
|
+
subject,
|
|
214
|
+
preview,
|
|
215
|
+
priority,
|
|
216
|
+
action_state,
|
|
217
|
+
visible_in_inbox,
|
|
218
|
+
visible_in_board_feed,
|
|
219
|
+
fetch_descriptor
|
|
220
|
+
)
|
|
221
|
+
values (
|
|
222
|
+
v_board_recipient,
|
|
223
|
+
v_board_member,
|
|
224
|
+
v_board,
|
|
225
|
+
'board',
|
|
226
|
+
'message',
|
|
227
|
+
v_message,
|
|
228
|
+
'Board update',
|
|
229
|
+
'Compact preview',
|
|
230
|
+
'high',
|
|
231
|
+
'new',
|
|
232
|
+
true,
|
|
233
|
+
true,
|
|
234
|
+
jsonb_build_object('surface', 'board', 'targetId', v_board::text, 'view', 'agent_board.v_board_feed', 'filters', jsonb_build_object(), 'reason', 'board delivery')
|
|
235
|
+
)
|
|
236
|
+
returning id into v_inbox_item;
|
|
237
|
+
|
|
238
|
+
insert into agent_board.callback_endpoints(owner_agent_id, endpoint_url, endpoint_status, verification_requested_at)
|
|
239
|
+
values (v_board_member, 'https://example.test/agent-board-callback', 'pending_verification', now())
|
|
240
|
+
returning id into v_endpoint;
|
|
241
|
+
|
|
242
|
+
insert into agent_board.callback_subscriptions(
|
|
243
|
+
endpoint_id,
|
|
244
|
+
agent_id,
|
|
245
|
+
subscription_scope,
|
|
246
|
+
event_category,
|
|
247
|
+
board_id,
|
|
248
|
+
board_membership_id
|
|
249
|
+
)
|
|
250
|
+
values (
|
|
251
|
+
v_endpoint,
|
|
252
|
+
v_board_member,
|
|
253
|
+
'board',
|
|
254
|
+
'board.post.delivered',
|
|
255
|
+
v_board,
|
|
256
|
+
v_membership
|
|
257
|
+
)
|
|
258
|
+
returning id into v_subscription;
|
|
259
|
+
|
|
260
|
+
insert into agent_board.callback_outbox_events(
|
|
261
|
+
endpoint_id,
|
|
262
|
+
subscription_id,
|
|
263
|
+
recipient_agent_id,
|
|
264
|
+
inbox_item_id,
|
|
265
|
+
board_id,
|
|
266
|
+
event_category,
|
|
267
|
+
delivery_mode,
|
|
268
|
+
source_kind,
|
|
269
|
+
source_message_id,
|
|
270
|
+
idempotency_key,
|
|
271
|
+
subject,
|
|
272
|
+
preview,
|
|
273
|
+
priority,
|
|
274
|
+
fetch_descriptor
|
|
275
|
+
)
|
|
276
|
+
values (
|
|
277
|
+
v_endpoint,
|
|
278
|
+
v_subscription,
|
|
279
|
+
v_board_member,
|
|
280
|
+
v_inbox_item,
|
|
281
|
+
v_board,
|
|
282
|
+
'board.post.delivered',
|
|
283
|
+
'board',
|
|
284
|
+
'message',
|
|
285
|
+
v_message,
|
|
286
|
+
'test-inboxschema-idempotency-key',
|
|
287
|
+
'Board update',
|
|
288
|
+
'Compact preview',
|
|
289
|
+
'high',
|
|
290
|
+
jsonb_build_object('surface', 'board', 'targetId', v_board::text, 'view', 'agent_board.v_board_feed', 'filters', jsonb_build_object(), 'reason', 'board delivery')
|
|
291
|
+
)
|
|
292
|
+
returning id into v_outbox;
|
|
293
|
+
|
|
294
|
+
insert into agent_board.callback_delivery_attempts(
|
|
295
|
+
outbox_event_id,
|
|
296
|
+
endpoint_id,
|
|
297
|
+
attempt_number,
|
|
298
|
+
attempt_outcome,
|
|
299
|
+
next_retry_at
|
|
300
|
+
)
|
|
301
|
+
values (
|
|
302
|
+
v_outbox,
|
|
303
|
+
v_endpoint,
|
|
304
|
+
1,
|
|
305
|
+
'retryable_failure',
|
|
306
|
+
now() + interval '5 minutes'
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
begin
|
|
310
|
+
insert into agent_board.delivery_recipients(delivery_mode, recipient_agent_id, source_kind, source_message_id)
|
|
311
|
+
values ('multicast', v_recipient, 'message', v_message);
|
|
312
|
+
raise exception 'duplicate recipient suppression failed';
|
|
313
|
+
exception
|
|
314
|
+
when unique_violation then null;
|
|
315
|
+
end;
|
|
316
|
+
|
|
317
|
+
begin
|
|
318
|
+
insert into agent_board.delivery_recipients(
|
|
319
|
+
delivery_mode,
|
|
320
|
+
recipient_agent_id,
|
|
321
|
+
source_kind,
|
|
322
|
+
source_message_id,
|
|
323
|
+
board_id
|
|
324
|
+
)
|
|
325
|
+
values ('board', v_recipient, 'message', v_message, v_board);
|
|
326
|
+
raise exception 'board recipient without membership should fail';
|
|
327
|
+
exception
|
|
328
|
+
when check_violation or foreign_key_violation then null;
|
|
329
|
+
end;
|
|
330
|
+
|
|
331
|
+
begin
|
|
332
|
+
insert into agent_board.callback_delivery_attempts(
|
|
333
|
+
outbox_event_id,
|
|
334
|
+
endpoint_id,
|
|
335
|
+
attempt_number,
|
|
336
|
+
attempt_outcome
|
|
337
|
+
)
|
|
338
|
+
values (
|
|
339
|
+
v_outbox,
|
|
340
|
+
v_endpoint,
|
|
341
|
+
2,
|
|
342
|
+
'retryable_failure'
|
|
343
|
+
);
|
|
344
|
+
raise exception 'retryable delivery attempt without next_retry_at should fail';
|
|
345
|
+
exception
|
|
346
|
+
when check_violation then null;
|
|
347
|
+
end;
|
|
348
|
+
end
|
|
349
|
+
$$;
|
|
350
|
+
|
|
351
|
+
select ok(true, 'schema-level behavior supports direct delivery, opt-in board fan-out, duplicate suppression, and durable outbox retry metadata');
|
|
352
|
+
|
|
353
|
+
select finish();
|
|
354
|
+
|
|
355
|
+
rollback;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-- pgTAP tests for non-fleet identity tables
|
|
2
|
+
begin;
|
|
3
|
+
select plan(18);
|
|
4
|
+
|
|
5
|
+
select has_table('agent_board', 'nonfleet_sessions', 'nonfleet_sessions table exists');
|
|
6
|
+
select has_column('agent_board', 'nonfleet_sessions', 'id', 'nonfleet_sessions has id');
|
|
7
|
+
select has_column('agent_board', 'nonfleet_sessions', 'session_id', 'nonfleet_sessions has session_id');
|
|
8
|
+
select has_column('agent_board', 'nonfleet_sessions', 'identity_class', 'nonfleet_sessions has identity_class');
|
|
9
|
+
select has_column('agent_board', 'nonfleet_sessions', 'supervisor_allowed', 'nonfleet_sessions has supervisor_allowed');
|
|
10
|
+
select has_column('agent_board', 'nonfleet_sessions', 'elevated_admin_allowed', 'nonfleet_sessions has elevated_admin_allowed');
|
|
11
|
+
select has_column('agent_board', 'nonfleet_sessions', 'parent_credential_id', 'nonfleet_sessions has parent_credential_id');
|
|
12
|
+
select has_column('agent_board', 'nonfleet_sessions', 'expires_at', 'nonfleet_sessions has expires_at');
|
|
13
|
+
select has_column('agent_board', 'nonfleet_sessions', 'revoked_at', 'nonfleet_sessions has revoked_at');
|
|
14
|
+
select col_default_is('agent_board', 'nonfleet_sessions', 'supervisor_allowed', 'false', 'supervisor_allowed defaults to false');
|
|
15
|
+
select col_default_is('agent_board', 'nonfleet_sessions', 'elevated_admin_allowed', 'false', 'elevated_admin_allowed defaults to false');
|
|
16
|
+
select has_index('agent_board', 'nonfleet_sessions', 'idx_nonfleet_sessions_session_id', 'session_id unique index exists');
|
|
17
|
+
|
|
18
|
+
select has_table('agent_board', 'nonfleet_invitations', 'nonfleet_invitations table exists');
|
|
19
|
+
select has_column('agent_board', 'nonfleet_invitations', 'trust_domain', 'nonfleet_invitations has trust_domain');
|
|
20
|
+
select has_column('agent_board', 'nonfleet_invitations', 'lifecycle_state', 'nonfleet_invitations has lifecycle_state');
|
|
21
|
+
select has_column('agent_board', 'nonfleet_invitations', 'invited_by_agent_id', 'nonfleet_invitations has invited_by_agent_id');
|
|
22
|
+
select has_column('agent_board', 'nonfleet_invitations', 'revoked_by_agent_id', 'nonfleet_invitations has revoked_by_agent_id');
|
|
23
|
+
select has_column('agent_board', 'nonfleet_invitations', 'accepted_at', 'nonfleet_invitations has accepted_at');
|
|
24
|
+
|
|
25
|
+
select * from finish();
|
|
26
|
+
rollback;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
begin;
|
|
2
|
+
select plan(6);
|
|
3
|
+
|
|
4
|
+
-- After the corrective migration, the five reasserted SELECT policies must be
|
|
5
|
+
-- BOARD-SCOPED (qualified outer-table references), never the leaking tautology
|
|
6
|
+
-- (bm.board_id = bm.board_id / dr.source_task_id = dr.id).
|
|
7
|
+
|
|
8
|
+
select ok(
|
|
9
|
+
pg_get_expr(polqual, polrelid) like '%bm.board_id = delivery_recipients.board_id%',
|
|
10
|
+
'delivery_recipients policy is board-scoped (qualified)')
|
|
11
|
+
from pg_policy where polname = 'agent_board_authenticated_select_delivery_recipients';
|
|
12
|
+
|
|
13
|
+
select ok(
|
|
14
|
+
pg_get_expr(polqual, polrelid) not like '%bm.board_id = bm.board_id%',
|
|
15
|
+
'delivery_recipients policy is NOT the leaking tautology')
|
|
16
|
+
from pg_policy where polname = 'agent_board_authenticated_select_delivery_recipients';
|
|
17
|
+
|
|
18
|
+
select ok(
|
|
19
|
+
pg_get_expr(polqual, polrelid) like '%bm.board_id = inbox_items.board_id%',
|
|
20
|
+
'inbox_items policy is board-scoped (qualified)')
|
|
21
|
+
from pg_policy where polname = 'agent_board_authenticated_select_inbox_items';
|
|
22
|
+
|
|
23
|
+
select ok(
|
|
24
|
+
pg_get_expr(polqual, polrelid) not like '%bm.board_id = bm.board_id%',
|
|
25
|
+
'inbox_items policy is NOT the leaking tautology')
|
|
26
|
+
from pg_policy where polname = 'agent_board_authenticated_select_inbox_items';
|
|
27
|
+
|
|
28
|
+
select ok(
|
|
29
|
+
pg_get_expr(polqual, polrelid) like '%dr.source_task_id = tasks.id%',
|
|
30
|
+
'tasks policy correlates delivery_recipients by tasks.id (qualified)')
|
|
31
|
+
from pg_policy where polname = 'agent_board_authenticated_select_tasks';
|
|
32
|
+
|
|
33
|
+
select ok(
|
|
34
|
+
pg_get_expr(polqual, polrelid) not like '%dr.source_task_id = dr.id%',
|
|
35
|
+
'tasks policy is NOT the leaking tautology')
|
|
36
|
+
from pg_policy where polname = 'agent_board_authenticated_select_tasks';
|
|
37
|
+
|
|
38
|
+
select * from finish();
|
|
39
|
+
rollback;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
SELECT plan(4);
|
|
2
|
+
|
|
3
|
+
SELECT has_table('agent_board', 'schema_version', 'schema_version table exists');
|
|
4
|
+
|
|
5
|
+
SELECT is(
|
|
6
|
+
(SELECT count(*)::int FROM agent_board.schema_version),
|
|
7
|
+
1,
|
|
8
|
+
'schema_version has exactly one row'
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
SELECT is(
|
|
12
|
+
(SELECT version FROM agent_board.schema_version),
|
|
13
|
+
7,
|
|
14
|
+
'schema_version.version is 7'
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
SELECT throws_ok(
|
|
18
|
+
$$INSERT INTO agent_board.schema_version (version) VALUES (8)$$,
|
|
19
|
+
'23505',
|
|
20
|
+
NULL,
|
|
21
|
+
'schema_version rejects a second row'
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
SELECT finish();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
create extension if not exists pgtap with schema extensions;
|
|
2
|
+
set search_path = extensions, public;
|
|
3
|
+
|
|
4
|
+
begin;
|
|
5
|
+
|
|
6
|
+
select plan(5);
|
|
7
|
+
|
|
8
|
+
select has_function(
|
|
9
|
+
'agent_board',
|
|
10
|
+
'get_schema_version',
|
|
11
|
+
array[]::text[],
|
|
12
|
+
'get_schema_version exists'
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
select ok(
|
|
16
|
+
has_function_privilege('authenticated', 'agent_board.get_schema_version()', 'EXECUTE'),
|
|
17
|
+
'authenticated agents can execute get_schema_version'
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
select ok(
|
|
21
|
+
not has_function_privilege('anon', 'agent_board.get_schema_version()', 'EXECUTE'),
|
|
22
|
+
'anonymous callers cannot execute get_schema_version'
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
select ok(
|
|
26
|
+
not has_table_privilege('authenticated', 'agent_board.schema_version', 'SELECT'),
|
|
27
|
+
'authenticated agents retain no direct schema_version table access'
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
set local role authenticated;
|
|
31
|
+
|
|
32
|
+
select is(
|
|
33
|
+
(select version from agent_board.get_schema_version()),
|
|
34
|
+
7,
|
|
35
|
+
'authenticated agents receive the metadata-only schema version'
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
select * from finish();
|
|
39
|
+
|
|
40
|
+
rollback;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
create extension if not exists pgtap with schema extensions;
|
|
2
|
+
set search_path = extensions, public, agent_board;
|
|
3
|
+
|
|
4
|
+
begin;
|
|
5
|
+
|
|
6
|
+
select plan(12);
|
|
7
|
+
|
|
8
|
+
select has_view('agent_board', 'v_signature_evidence_status', 'signature evidence status view exists');
|
|
9
|
+
select has_column('agent_board', 'v_thread_feed', 'verification_status', 'thread feed exposes verification_status');
|
|
10
|
+
select has_column('agent_board', 'v_thread_feed', 'key_fingerprint', 'thread feed exposes key_fingerprint');
|
|
11
|
+
select has_column('agent_board', 'v_thread_feed', 'key_version', 'thread feed exposes key_version');
|
|
12
|
+
select has_column('agent_board', 'v_thread_feed', 'algorithm', 'thread feed exposes algorithm');
|
|
13
|
+
select has_column('agent_board', 'v_thread_feed', 'verified_at', 'thread feed exposes verified_at');
|
|
14
|
+
select has_column('agent_board', 'v_thread_feed', 'failure_class', 'thread feed exposes failure_class');
|
|
15
|
+
select has_column('agent_board', 'v_task_tree', 'verification_status', 'task tree exposes verification_status');
|
|
16
|
+
select has_column('agent_board', 'v_agent_dashboard', 'latest_signature_verification_status', 'dashboard exposes latest signature status');
|
|
17
|
+
|
|
18
|
+
select ok(
|
|
19
|
+
(
|
|
20
|
+
select count(*) = 4
|
|
21
|
+
from pg_class c
|
|
22
|
+
where c.relnamespace = 'agent_board'::regnamespace
|
|
23
|
+
and c.relname in (
|
|
24
|
+
'agent_signing_keys',
|
|
25
|
+
'agent_signing_key_challenges',
|
|
26
|
+
'signature_evidence',
|
|
27
|
+
'signature_replay_state'
|
|
28
|
+
)
|
|
29
|
+
and c.relrowsecurity
|
|
30
|
+
),
|
|
31
|
+
'SIGNSTORE tables have RLS enabled'
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
do $$
|
|
35
|
+
declare
|
|
36
|
+
v_author uuid := gen_random_uuid();
|
|
37
|
+
v_recipient uuid := gen_random_uuid();
|
|
38
|
+
v_thread uuid := gen_random_uuid();
|
|
39
|
+
v_message uuid := gen_random_uuid();
|
|
40
|
+
v_key uuid;
|
|
41
|
+
v_seen bigint;
|
|
42
|
+
begin
|
|
43
|
+
insert into agent_board.agents(id, agent_type, handle, display_name)
|
|
44
|
+
values
|
|
45
|
+
(v_author, 'agent', 'signread-author', 'SIGNREAD Author'),
|
|
46
|
+
(v_recipient, 'agent', 'signread-recipient', 'SIGNREAD Recipient');
|
|
47
|
+
|
|
48
|
+
insert into agent_board.threads(id, created_by_agent_id, subject)
|
|
49
|
+
values (v_thread, v_author, 'SIGNREAD thread');
|
|
50
|
+
|
|
51
|
+
insert into agent_board.messages(id, thread_id, author_agent_id, message_sequence, body)
|
|
52
|
+
values (v_message, v_thread, v_author, 1, 'visible signed body');
|
|
53
|
+
|
|
54
|
+
insert into agent_board.delivery_recipients(delivery_mode, recipient_agent_id, source_kind, source_message_id)
|
|
55
|
+
values ('direct', v_recipient, 'message', v_message);
|
|
56
|
+
|
|
57
|
+
insert into agent_board.agent_signing_keys(
|
|
58
|
+
agent_id,
|
|
59
|
+
key_id,
|
|
60
|
+
key_version,
|
|
61
|
+
public_key,
|
|
62
|
+
public_key_format,
|
|
63
|
+
fingerprint,
|
|
64
|
+
status,
|
|
65
|
+
proof_state
|
|
66
|
+
)
|
|
67
|
+
values (
|
|
68
|
+
v_author,
|
|
69
|
+
'primary',
|
|
70
|
+
1,
|
|
71
|
+
repeat('A', 43),
|
|
72
|
+
'raw_ed25519_base64url',
|
|
73
|
+
'sha256:' || repeat('Q', 43),
|
|
74
|
+
'active',
|
|
75
|
+
'proven'
|
|
76
|
+
)
|
|
77
|
+
returning id into v_key;
|
|
78
|
+
|
|
79
|
+
insert into agent_board.signature_evidence(
|
|
80
|
+
agent_id,
|
|
81
|
+
signing_key_id,
|
|
82
|
+
key_id,
|
|
83
|
+
key_version,
|
|
84
|
+
source_kind,
|
|
85
|
+
source_message_id,
|
|
86
|
+
operation,
|
|
87
|
+
payload_digest,
|
|
88
|
+
verification_status,
|
|
89
|
+
verified_at
|
|
90
|
+
)
|
|
91
|
+
values (
|
|
92
|
+
v_author,
|
|
93
|
+
v_key,
|
|
94
|
+
'primary',
|
|
95
|
+
1,
|
|
96
|
+
'message',
|
|
97
|
+
v_message,
|
|
98
|
+
'agent_board.post_message',
|
|
99
|
+
'sha256:' || repeat('a', 64),
|
|
100
|
+
'verified',
|
|
101
|
+
now()
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
select count(*) into v_seen
|
|
105
|
+
from agent_board.v_thread_feed
|
|
106
|
+
where message_id = v_message
|
|
107
|
+
and verification_status = 'verified'
|
|
108
|
+
and key_fingerprint = 'sha256:' || repeat('Q', 43)
|
|
109
|
+
and key_version = 1
|
|
110
|
+
and algorithm = 'Ed25519';
|
|
111
|
+
|
|
112
|
+
if v_seen <> 1 then
|
|
113
|
+
raise exception 'verification-aware thread feed did not expose signature status';
|
|
114
|
+
end if;
|
|
115
|
+
end
|
|
116
|
+
$$;
|
|
117
|
+
|
|
118
|
+
select pass('verification-aware read models expose metadata-safe signature status');
|
|
119
|
+
|
|
120
|
+
select ok(
|
|
121
|
+
not exists (
|
|
122
|
+
select 1
|
|
123
|
+
from information_schema.columns
|
|
124
|
+
where table_schema = 'agent_board'
|
|
125
|
+
and table_name in ('v_thread_feed', 'v_task_tree', 'v_agent_dashboard', 'v_signature_evidence_status')
|
|
126
|
+
and column_name ~* 'raw|private|secret|signature$'
|
|
127
|
+
),
|
|
128
|
+
'default read models omit raw signatures and private material'
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
select finish();
|
|
132
|
+
|
|
133
|
+
rollback;
|