@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,493 @@
|
|
|
1
|
+
alter table agent_board.boards
|
|
2
|
+
add column if not exists visibility text not null default 'private';
|
|
3
|
+
|
|
4
|
+
alter table agent_board.boards
|
|
5
|
+
drop constraint if exists boards_visibility_check;
|
|
6
|
+
|
|
7
|
+
alter table agent_board.boards
|
|
8
|
+
add constraint boards_visibility_check
|
|
9
|
+
check (visibility in ('private', 'invite_only', 'open', 'directory_hidden'));
|
|
10
|
+
|
|
11
|
+
alter table agent_board.board_memberships
|
|
12
|
+
drop constraint if exists board_memberships_membership_state_check;
|
|
13
|
+
|
|
14
|
+
alter table agent_board.board_memberships
|
|
15
|
+
add constraint board_memberships_membership_state_check
|
|
16
|
+
check (membership_state in ('requested', 'invited', 'joined', 'subscribed', 'left', 'removed'));
|
|
17
|
+
|
|
18
|
+
alter table agent_board.board_memberships
|
|
19
|
+
drop constraint if exists board_memberships_state_left_at;
|
|
20
|
+
|
|
21
|
+
alter table agent_board.board_memberships
|
|
22
|
+
add constraint board_memberships_state_left_at check (
|
|
23
|
+
(membership_state in ('requested', 'invited', 'joined', 'subscribed') and left_at is null)
|
|
24
|
+
or (membership_state in ('left', 'removed') and left_at is not null)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
create index if not exists idx_agent_board_boards_visibility
|
|
28
|
+
on agent_board.boards(visibility, status, name, id);
|
|
29
|
+
|
|
30
|
+
create index if not exists idx_agent_board_board_memberships_agent_state
|
|
31
|
+
on agent_board.board_memberships(agent_id, membership_state, updated_at desc);
|
|
32
|
+
|
|
33
|
+
drop function if exists agent_board.create_board(uuid, text, text, text, jsonb);
|
|
34
|
+
|
|
35
|
+
create or replace function agent_board.create_board(
|
|
36
|
+
p_created_by_agent_id uuid,
|
|
37
|
+
p_slug text,
|
|
38
|
+
p_name text,
|
|
39
|
+
p_description text default null,
|
|
40
|
+
p_metadata jsonb default '{}'::jsonb,
|
|
41
|
+
p_visibility text default 'private'
|
|
42
|
+
)
|
|
43
|
+
returns table(board_id uuid, slug text, name text, status text, visibility text, created_at timestamptz, updated_at timestamptz)
|
|
44
|
+
language plpgsql
|
|
45
|
+
security definer
|
|
46
|
+
set search_path = agent_board, public
|
|
47
|
+
as $$
|
|
48
|
+
begin
|
|
49
|
+
if nullif(trim(p_slug), '') is null or nullif(trim(p_name), '') is null then
|
|
50
|
+
raise exception 'board slug and name are required' using errcode = '22023';
|
|
51
|
+
end if;
|
|
52
|
+
if coalesce(p_visibility, 'private') not in ('private', 'invite_only', 'open', 'directory_hidden') then
|
|
53
|
+
raise exception 'invalid board visibility' using errcode = '22023';
|
|
54
|
+
end if;
|
|
55
|
+
if not exists (select 1 from agent_board.agents where id = p_created_by_agent_id) then
|
|
56
|
+
raise exception 'agent not found' using errcode = 'P0002';
|
|
57
|
+
end if;
|
|
58
|
+
|
|
59
|
+
return query
|
|
60
|
+
insert into agent_board.boards as b(slug, name, description, created_by_agent_id, visibility, metadata)
|
|
61
|
+
values (
|
|
62
|
+
trim(p_slug),
|
|
63
|
+
trim(p_name),
|
|
64
|
+
nullif(trim(p_description), ''),
|
|
65
|
+
p_created_by_agent_id,
|
|
66
|
+
coalesce(p_visibility, 'private'),
|
|
67
|
+
coalesce(p_metadata, '{}'::jsonb)
|
|
68
|
+
)
|
|
69
|
+
on conflict on constraint boards_slug_key do update
|
|
70
|
+
set name = excluded.name,
|
|
71
|
+
description = excluded.description,
|
|
72
|
+
status = 'active',
|
|
73
|
+
visibility = excluded.visibility,
|
|
74
|
+
metadata = coalesce(b.metadata, '{}'::jsonb) || coalesce(excluded.metadata, '{}'::jsonb),
|
|
75
|
+
updated_at = now()
|
|
76
|
+
returning b.id, b.slug, b.name, b.status, b.visibility, b.created_at, b.updated_at;
|
|
77
|
+
end;
|
|
78
|
+
$$;
|
|
79
|
+
|
|
80
|
+
create or replace function agent_board._upsert_board_membership_state(
|
|
81
|
+
p_board_id uuid,
|
|
82
|
+
p_agent_id uuid,
|
|
83
|
+
p_membership_state text,
|
|
84
|
+
p_created_by_agent_id uuid default null,
|
|
85
|
+
p_metadata jsonb default '{}'::jsonb
|
|
86
|
+
)
|
|
87
|
+
returns agent_board.board_memberships
|
|
88
|
+
language plpgsql
|
|
89
|
+
security definer
|
|
90
|
+
set search_path = agent_board, public
|
|
91
|
+
as $$
|
|
92
|
+
declare
|
|
93
|
+
v_membership agent_board.board_memberships%rowtype;
|
|
94
|
+
begin
|
|
95
|
+
if p_membership_state not in ('requested', 'invited', 'joined', 'subscribed', 'left', 'removed') then
|
|
96
|
+
raise exception 'invalid board membership state' using errcode = '22023';
|
|
97
|
+
end if;
|
|
98
|
+
|
|
99
|
+
select *
|
|
100
|
+
into v_membership
|
|
101
|
+
from agent_board.board_memberships bm
|
|
102
|
+
where bm.board_id = p_board_id
|
|
103
|
+
and bm.agent_id = p_agent_id
|
|
104
|
+
order by bm.created_at desc
|
|
105
|
+
limit 1
|
|
106
|
+
for update;
|
|
107
|
+
|
|
108
|
+
if v_membership.id is null then
|
|
109
|
+
insert into agent_board.board_memberships(board_id, agent_id, membership_state, created_by_agent_id, left_at, metadata)
|
|
110
|
+
values (
|
|
111
|
+
p_board_id,
|
|
112
|
+
p_agent_id,
|
|
113
|
+
p_membership_state,
|
|
114
|
+
p_created_by_agent_id,
|
|
115
|
+
case when p_membership_state in ('left', 'removed') then now() else null end,
|
|
116
|
+
coalesce(p_metadata, '{}'::jsonb)
|
|
117
|
+
)
|
|
118
|
+
returning * into v_membership;
|
|
119
|
+
else
|
|
120
|
+
update agent_board.board_memberships bm
|
|
121
|
+
set membership_state = p_membership_state,
|
|
122
|
+
created_by_agent_id = coalesce(p_created_by_agent_id, bm.created_by_agent_id),
|
|
123
|
+
left_at = case when p_membership_state in ('left', 'removed') then now() else null end,
|
|
124
|
+
metadata = coalesce(bm.metadata, '{}'::jsonb) || coalesce(p_metadata, '{}'::jsonb),
|
|
125
|
+
updated_at = now()
|
|
126
|
+
where bm.id = v_membership.id
|
|
127
|
+
returning * into v_membership;
|
|
128
|
+
end if;
|
|
129
|
+
|
|
130
|
+
return v_membership;
|
|
131
|
+
end;
|
|
132
|
+
$$;
|
|
133
|
+
|
|
134
|
+
create or replace function agent_board.join_board(
|
|
135
|
+
p_board_id uuid,
|
|
136
|
+
p_agent_id uuid,
|
|
137
|
+
p_membership_state text default 'joined',
|
|
138
|
+
p_created_by_agent_id uuid default null,
|
|
139
|
+
p_metadata jsonb default '{}'::jsonb
|
|
140
|
+
)
|
|
141
|
+
returns table(membership_id uuid, board_id uuid, agent_id uuid, membership_state text, created_at timestamptz, updated_at timestamptz)
|
|
142
|
+
language plpgsql
|
|
143
|
+
security definer
|
|
144
|
+
set search_path = agent_board, public
|
|
145
|
+
as $$
|
|
146
|
+
declare
|
|
147
|
+
v_board agent_board.boards%rowtype;
|
|
148
|
+
v_existing_state text;
|
|
149
|
+
v_membership agent_board.board_memberships%rowtype;
|
|
150
|
+
v_actor uuid := agent_board.current_agent_id();
|
|
151
|
+
begin
|
|
152
|
+
if p_membership_state not in ('joined', 'subscribed') then
|
|
153
|
+
raise exception 'join_board only accepts joined or subscribed' using errcode = '22023';
|
|
154
|
+
end if;
|
|
155
|
+
if not exists (select 1 from agent_board.agents where id = p_agent_id) then
|
|
156
|
+
raise exception 'agent not found' using errcode = 'P0002';
|
|
157
|
+
end if;
|
|
158
|
+
if p_created_by_agent_id is not null and not exists (select 1 from agent_board.agents where id = p_created_by_agent_id) then
|
|
159
|
+
raise exception 'creator agent not found' using errcode = 'P0002';
|
|
160
|
+
end if;
|
|
161
|
+
|
|
162
|
+
select * into v_board from agent_board.boards where id = p_board_id and status = 'active';
|
|
163
|
+
if v_board.id is null then
|
|
164
|
+
raise exception 'board not found' using errcode = 'P0002';
|
|
165
|
+
end if;
|
|
166
|
+
|
|
167
|
+
select bm.membership_state
|
|
168
|
+
into v_existing_state
|
|
169
|
+
from agent_board.board_memberships bm
|
|
170
|
+
where bm.board_id = p_board_id
|
|
171
|
+
and bm.agent_id = p_agent_id
|
|
172
|
+
order by bm.created_at desc
|
|
173
|
+
limit 1;
|
|
174
|
+
|
|
175
|
+
if v_actor is not null and v_actor <> p_agent_id and v_actor <> v_board.created_by_agent_id then
|
|
176
|
+
raise exception 'board membership actor mismatch' using errcode = '42501';
|
|
177
|
+
end if;
|
|
178
|
+
|
|
179
|
+
if v_actor is not null
|
|
180
|
+
and v_actor = p_agent_id
|
|
181
|
+
and v_board.created_by_agent_id <> v_actor
|
|
182
|
+
and not (
|
|
183
|
+
v_board.visibility = 'open'
|
|
184
|
+
or (v_board.visibility = 'invite_only' and v_existing_state = 'invited')
|
|
185
|
+
or v_existing_state in ('joined', 'subscribed')
|
|
186
|
+
) then
|
|
187
|
+
raise exception 'board is not joinable by this agent' using errcode = 'P0002';
|
|
188
|
+
end if;
|
|
189
|
+
|
|
190
|
+
v_membership := agent_board._upsert_board_membership_state(
|
|
191
|
+
p_board_id,
|
|
192
|
+
p_agent_id,
|
|
193
|
+
p_membership_state,
|
|
194
|
+
coalesce(p_created_by_agent_id, p_agent_id),
|
|
195
|
+
p_metadata
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return query
|
|
199
|
+
select v_membership.id, v_membership.board_id, v_membership.agent_id, v_membership.membership_state, v_membership.created_at, v_membership.updated_at;
|
|
200
|
+
end;
|
|
201
|
+
$$;
|
|
202
|
+
|
|
203
|
+
create or replace function agent_board.update_board_membership(
|
|
204
|
+
p_board_id uuid,
|
|
205
|
+
p_agent_id uuid,
|
|
206
|
+
p_membership_state text,
|
|
207
|
+
p_metadata jsonb default '{}'::jsonb
|
|
208
|
+
)
|
|
209
|
+
returns table(membership_id uuid, board_id uuid, agent_id uuid, membership_state text, left_at timestamptz, updated_at timestamptz)
|
|
210
|
+
language plpgsql
|
|
211
|
+
security definer
|
|
212
|
+
set search_path = agent_board, public
|
|
213
|
+
as $$
|
|
214
|
+
declare
|
|
215
|
+
v_membership agent_board.board_memberships%rowtype;
|
|
216
|
+
begin
|
|
217
|
+
if not exists (
|
|
218
|
+
select 1
|
|
219
|
+
from agent_board.board_memberships bm
|
|
220
|
+
where bm.board_id = p_board_id
|
|
221
|
+
and bm.agent_id = p_agent_id
|
|
222
|
+
) then
|
|
223
|
+
raise exception 'board membership not found' using errcode = 'P0002';
|
|
224
|
+
end if;
|
|
225
|
+
|
|
226
|
+
select * into v_membership
|
|
227
|
+
from agent_board._upsert_board_membership_state(
|
|
228
|
+
p_board_id,
|
|
229
|
+
p_agent_id,
|
|
230
|
+
p_membership_state,
|
|
231
|
+
null,
|
|
232
|
+
p_metadata
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
return query
|
|
236
|
+
select v_membership.id, v_membership.board_id, v_membership.agent_id, v_membership.membership_state, v_membership.left_at, v_membership.updated_at;
|
|
237
|
+
end;
|
|
238
|
+
$$;
|
|
239
|
+
|
|
240
|
+
create or replace function agent_board.request_join_board(
|
|
241
|
+
p_board_id uuid,
|
|
242
|
+
p_agent_id uuid,
|
|
243
|
+
p_metadata jsonb default '{}'::jsonb
|
|
244
|
+
)
|
|
245
|
+
returns table(membership_id uuid, board_id uuid, agent_id uuid, membership_state text, created_at timestamptz, updated_at timestamptz)
|
|
246
|
+
language plpgsql
|
|
247
|
+
security definer
|
|
248
|
+
set search_path = agent_board, public
|
|
249
|
+
as $$
|
|
250
|
+
declare
|
|
251
|
+
v_board agent_board.boards%rowtype;
|
|
252
|
+
v_membership agent_board.board_memberships%rowtype;
|
|
253
|
+
v_actor uuid := agent_board.current_agent_id();
|
|
254
|
+
begin
|
|
255
|
+
select * into v_board
|
|
256
|
+
from agent_board.boards
|
|
257
|
+
where id = p_board_id
|
|
258
|
+
and status = 'active'
|
|
259
|
+
and visibility in ('open', 'invite_only');
|
|
260
|
+
|
|
261
|
+
if v_board.id is null then
|
|
262
|
+
raise exception 'board not found' using errcode = 'P0002';
|
|
263
|
+
end if;
|
|
264
|
+
if v_actor is not null and v_actor <> p_agent_id then
|
|
265
|
+
raise exception 'board request actor mismatch' using errcode = '42501';
|
|
266
|
+
end if;
|
|
267
|
+
if not exists (select 1 from agent_board.agents where id = p_agent_id) then
|
|
268
|
+
raise exception 'agent not found' using errcode = 'P0002';
|
|
269
|
+
end if;
|
|
270
|
+
|
|
271
|
+
v_membership := agent_board._upsert_board_membership_state(
|
|
272
|
+
p_board_id,
|
|
273
|
+
p_agent_id,
|
|
274
|
+
'requested',
|
|
275
|
+
p_agent_id,
|
|
276
|
+
p_metadata
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
return query
|
|
280
|
+
select v_membership.id, v_membership.board_id, v_membership.agent_id, v_membership.membership_state, v_membership.created_at, v_membership.updated_at;
|
|
281
|
+
end;
|
|
282
|
+
$$;
|
|
283
|
+
|
|
284
|
+
create or replace function agent_board.invite_board_member(
|
|
285
|
+
p_board_id uuid,
|
|
286
|
+
p_agent_id uuid,
|
|
287
|
+
p_created_by_agent_id uuid,
|
|
288
|
+
p_metadata jsonb default '{}'::jsonb
|
|
289
|
+
)
|
|
290
|
+
returns table(membership_id uuid, board_id uuid, agent_id uuid, membership_state text, created_at timestamptz, updated_at timestamptz)
|
|
291
|
+
language plpgsql
|
|
292
|
+
security definer
|
|
293
|
+
set search_path = agent_board, public
|
|
294
|
+
as $$
|
|
295
|
+
declare
|
|
296
|
+
v_board agent_board.boards%rowtype;
|
|
297
|
+
v_membership agent_board.board_memberships%rowtype;
|
|
298
|
+
v_actor uuid := agent_board.current_agent_id();
|
|
299
|
+
begin
|
|
300
|
+
select * into v_board from agent_board.boards where id = p_board_id and status = 'active';
|
|
301
|
+
if v_board.id is null then
|
|
302
|
+
raise exception 'board not found' using errcode = 'P0002';
|
|
303
|
+
end if;
|
|
304
|
+
if v_actor is not null
|
|
305
|
+
and v_actor <> v_board.created_by_agent_id
|
|
306
|
+
and not exists (
|
|
307
|
+
select 1
|
|
308
|
+
from agent_board.board_memberships bm
|
|
309
|
+
where bm.board_id = p_board_id
|
|
310
|
+
and bm.agent_id = v_actor
|
|
311
|
+
and bm.membership_state in ('joined', 'subscribed')
|
|
312
|
+
) then
|
|
313
|
+
raise exception 'board invite requires active membership' using errcode = '42501';
|
|
314
|
+
end if;
|
|
315
|
+
if not exists (select 1 from agent_board.agents where id = p_agent_id) then
|
|
316
|
+
raise exception 'agent not found' using errcode = 'P0002';
|
|
317
|
+
end if;
|
|
318
|
+
if not exists (select 1 from agent_board.agents where id = p_created_by_agent_id) then
|
|
319
|
+
raise exception 'creator agent not found' using errcode = 'P0002';
|
|
320
|
+
end if;
|
|
321
|
+
|
|
322
|
+
v_membership := agent_board._upsert_board_membership_state(
|
|
323
|
+
p_board_id,
|
|
324
|
+
p_agent_id,
|
|
325
|
+
'invited',
|
|
326
|
+
p_created_by_agent_id,
|
|
327
|
+
p_metadata
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
return query
|
|
331
|
+
select v_membership.id, v_membership.board_id, v_membership.agent_id, v_membership.membership_state, v_membership.created_at, v_membership.updated_at;
|
|
332
|
+
end;
|
|
333
|
+
$$;
|
|
334
|
+
|
|
335
|
+
create or replace function agent_board.resolve_board_request(
|
|
336
|
+
p_board_id uuid,
|
|
337
|
+
p_agent_id uuid,
|
|
338
|
+
p_membership_state text,
|
|
339
|
+
p_created_by_agent_id uuid,
|
|
340
|
+
p_metadata jsonb default '{}'::jsonb
|
|
341
|
+
)
|
|
342
|
+
returns table(membership_id uuid, board_id uuid, agent_id uuid, membership_state text, left_at timestamptz, updated_at timestamptz)
|
|
343
|
+
language plpgsql
|
|
344
|
+
security definer
|
|
345
|
+
set search_path = agent_board, public
|
|
346
|
+
as $$
|
|
347
|
+
declare
|
|
348
|
+
v_board agent_board.boards%rowtype;
|
|
349
|
+
v_existing_state text;
|
|
350
|
+
v_membership agent_board.board_memberships%rowtype;
|
|
351
|
+
v_actor uuid := agent_board.current_agent_id();
|
|
352
|
+
begin
|
|
353
|
+
if p_membership_state not in ('joined', 'subscribed', 'removed') then
|
|
354
|
+
raise exception 'resolve_board_request only accepts joined, subscribed, or removed' using errcode = '22023';
|
|
355
|
+
end if;
|
|
356
|
+
select * into v_board from agent_board.boards where id = p_board_id and status = 'active';
|
|
357
|
+
if v_board.id is null then
|
|
358
|
+
raise exception 'board not found' using errcode = 'P0002';
|
|
359
|
+
end if;
|
|
360
|
+
if v_actor is not null
|
|
361
|
+
and v_actor <> v_board.created_by_agent_id
|
|
362
|
+
and not exists (
|
|
363
|
+
select 1
|
|
364
|
+
from agent_board.board_memberships bm
|
|
365
|
+
where bm.board_id = p_board_id
|
|
366
|
+
and bm.agent_id = v_actor
|
|
367
|
+
and bm.membership_state in ('joined', 'subscribed')
|
|
368
|
+
) then
|
|
369
|
+
raise exception 'board request resolution requires active membership' using errcode = '42501';
|
|
370
|
+
end if;
|
|
371
|
+
|
|
372
|
+
select bm.membership_state
|
|
373
|
+
into v_existing_state
|
|
374
|
+
from agent_board.board_memberships bm
|
|
375
|
+
where bm.board_id = p_board_id
|
|
376
|
+
and bm.agent_id = p_agent_id
|
|
377
|
+
order by bm.created_at desc
|
|
378
|
+
limit 1;
|
|
379
|
+
|
|
380
|
+
if v_existing_state not in ('requested', 'invited') then
|
|
381
|
+
raise exception 'pending board membership not found' using errcode = 'P0002';
|
|
382
|
+
end if;
|
|
383
|
+
|
|
384
|
+
v_membership := agent_board._upsert_board_membership_state(
|
|
385
|
+
p_board_id,
|
|
386
|
+
p_agent_id,
|
|
387
|
+
p_membership_state,
|
|
388
|
+
p_created_by_agent_id,
|
|
389
|
+
p_metadata
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
return query
|
|
393
|
+
select v_membership.id, v_membership.board_id, v_membership.agent_id, v_membership.membership_state, v_membership.left_at, v_membership.updated_at;
|
|
394
|
+
end;
|
|
395
|
+
$$;
|
|
396
|
+
|
|
397
|
+
drop view if exists agent_board.v_board_directory;
|
|
398
|
+
|
|
399
|
+
create view agent_board.v_board_directory
|
|
400
|
+
with (security_invoker = true)
|
|
401
|
+
as
|
|
402
|
+
select
|
|
403
|
+
b.id as board_id,
|
|
404
|
+
b.slug as board_slug,
|
|
405
|
+
b.name as board_name,
|
|
406
|
+
b.description as board_description,
|
|
407
|
+
b.status as board_status,
|
|
408
|
+
b.visibility as board_visibility,
|
|
409
|
+
b.created_by_agent_id,
|
|
410
|
+
creator.handle as created_by_handle,
|
|
411
|
+
coalesce(members.active_member_count, 0)::bigint as active_member_count,
|
|
412
|
+
current_membership.membership_state as current_membership_state,
|
|
413
|
+
coalesce(board_activity.latest_board_activity_at, b.updated_at) as latest_board_activity_at,
|
|
414
|
+
b.created_at,
|
|
415
|
+
b.updated_at
|
|
416
|
+
from agent_board.boards b
|
|
417
|
+
join agent_board.agents creator on creator.id = b.created_by_agent_id
|
|
418
|
+
left join lateral (
|
|
419
|
+
select count(*) as active_member_count
|
|
420
|
+
from agent_board.board_memberships bm
|
|
421
|
+
where bm.board_id = b.id
|
|
422
|
+
and bm.membership_state in ('joined', 'subscribed')
|
|
423
|
+
) members on true
|
|
424
|
+
left join lateral (
|
|
425
|
+
select bm.membership_state
|
|
426
|
+
from agent_board.board_memberships bm
|
|
427
|
+
where bm.board_id = b.id
|
|
428
|
+
and bm.agent_id = agent_board.current_agent_id()
|
|
429
|
+
order by bm.created_at desc
|
|
430
|
+
limit 1
|
|
431
|
+
) current_membership on true
|
|
432
|
+
left join lateral (
|
|
433
|
+
select max(ii.created_at) as latest_board_activity_at
|
|
434
|
+
from agent_board.inbox_items ii
|
|
435
|
+
where ii.board_id = b.id
|
|
436
|
+
and ii.visible_in_board_feed = true
|
|
437
|
+
) board_activity on true
|
|
438
|
+
where b.status = 'active'
|
|
439
|
+
and b.visibility <> 'directory_hidden'
|
|
440
|
+
and (
|
|
441
|
+
b.visibility = 'open'
|
|
442
|
+
or b.created_by_agent_id = agent_board.current_agent_id()
|
|
443
|
+
or current_membership.membership_state is not null
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
drop policy if exists agent_board_authenticated_select_boards on agent_board.boards;
|
|
447
|
+
create policy agent_board_authenticated_select_boards
|
|
448
|
+
on agent_board.boards
|
|
449
|
+
as permissive
|
|
450
|
+
for select
|
|
451
|
+
to authenticated
|
|
452
|
+
using (
|
|
453
|
+
created_by_agent_id = agent_board.current_agent_id()
|
|
454
|
+
or (visibility = 'open' and status = 'active')
|
|
455
|
+
or exists (
|
|
456
|
+
select 1
|
|
457
|
+
from agent_board.board_memberships bm
|
|
458
|
+
where bm.board_id = boards.id
|
|
459
|
+
and bm.agent_id = agent_board.current_agent_id()
|
|
460
|
+
and bm.membership_state in ('requested', 'invited', 'joined', 'subscribed')
|
|
461
|
+
)
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
drop policy if exists agent_board_authenticated_select_board_memberships on agent_board.board_memberships;
|
|
465
|
+
create policy agent_board_authenticated_select_board_memberships
|
|
466
|
+
on agent_board.board_memberships
|
|
467
|
+
as permissive
|
|
468
|
+
for select
|
|
469
|
+
to authenticated
|
|
470
|
+
using (
|
|
471
|
+
agent_id = agent_board.current_agent_id()
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
revoke all on function agent_board.create_board(uuid, text, text, text, jsonb, text) from public, anon;
|
|
475
|
+
revoke all on function agent_board._upsert_board_membership_state(uuid, uuid, text, uuid, jsonb) from public, anon, authenticated;
|
|
476
|
+
revoke all on function agent_board.join_board(uuid, uuid, text, uuid, jsonb) from public, anon;
|
|
477
|
+
revoke all on function agent_board.update_board_membership(uuid, uuid, text, jsonb) from public, anon;
|
|
478
|
+
revoke all on function agent_board.request_join_board(uuid, uuid, jsonb) from public, anon;
|
|
479
|
+
revoke all on function agent_board.invite_board_member(uuid, uuid, uuid, jsonb) from public, anon;
|
|
480
|
+
revoke all on function agent_board.resolve_board_request(uuid, uuid, text, uuid, jsonb) from public, anon;
|
|
481
|
+
|
|
482
|
+
grant select on agent_board.v_board_directory to authenticated, service_role;
|
|
483
|
+
|
|
484
|
+
grant execute on function agent_board.create_board(uuid, text, text, text, jsonb, text) to authenticated, service_role;
|
|
485
|
+
grant execute on function agent_board.join_board(uuid, uuid, text, uuid, jsonb) to authenticated, service_role;
|
|
486
|
+
grant execute on function agent_board.update_board_membership(uuid, uuid, text, jsonb) to authenticated, service_role;
|
|
487
|
+
grant execute on function agent_board.request_join_board(uuid, uuid, jsonb) to authenticated, service_role;
|
|
488
|
+
grant execute on function agent_board.invite_board_member(uuid, uuid, uuid, jsonb) to authenticated, service_role;
|
|
489
|
+
grant execute on function agent_board.resolve_board_request(uuid, uuid, text, uuid, jsonb) to authenticated, service_role;
|
|
490
|
+
|
|
491
|
+
insert into agent_board.schema_version (version)
|
|
492
|
+
values (4)
|
|
493
|
+
on conflict ((true)) do update set version = excluded.version;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
create or replace function agent_board.register_callback_endpoint(
|
|
2
|
+
p_owner_agent_id uuid,
|
|
3
|
+
p_endpoint_url text,
|
|
4
|
+
p_metadata jsonb default '{}'::jsonb
|
|
5
|
+
)
|
|
6
|
+
returns table(endpoint_id uuid, owner_agent_id uuid, endpoint_status text, signing_key_version integer, verification_requested_at timestamptz, updated_at timestamptz)
|
|
7
|
+
language plpgsql
|
|
8
|
+
security definer
|
|
9
|
+
set search_path = agent_board, public
|
|
10
|
+
as $$
|
|
11
|
+
declare
|
|
12
|
+
v_actor_agent_id uuid;
|
|
13
|
+
v_metadata jsonb;
|
|
14
|
+
begin
|
|
15
|
+
v_actor_agent_id := agent_board._resolved_actor_agent_id(p_owner_agent_id, 'p_owner_agent_id');
|
|
16
|
+
|
|
17
|
+
if nullif(trim(p_endpoint_url), '') is null or trim(p_endpoint_url) !~ '^https://.+$' then
|
|
18
|
+
raise exception 'callback endpoint URL must be https' using errcode = '22023';
|
|
19
|
+
end if;
|
|
20
|
+
|
|
21
|
+
v_metadata := coalesce(p_metadata, '{}'::jsonb)
|
|
22
|
+
|| jsonb_build_object(
|
|
23
|
+
'verificationMethod',
|
|
24
|
+
'administrative_enablement',
|
|
25
|
+
'controlProofRequired',
|
|
26
|
+
true
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return query
|
|
30
|
+
insert into agent_board.callback_endpoints as ce(
|
|
31
|
+
owner_agent_id,
|
|
32
|
+
endpoint_url,
|
|
33
|
+
endpoint_status,
|
|
34
|
+
verification_requested_at,
|
|
35
|
+
verified_at,
|
|
36
|
+
disabled_at,
|
|
37
|
+
disabled_reason,
|
|
38
|
+
metadata
|
|
39
|
+
)
|
|
40
|
+
values (
|
|
41
|
+
v_actor_agent_id,
|
|
42
|
+
trim(p_endpoint_url),
|
|
43
|
+
'pending_verification',
|
|
44
|
+
now(),
|
|
45
|
+
null,
|
|
46
|
+
null,
|
|
47
|
+
null,
|
|
48
|
+
v_metadata
|
|
49
|
+
)
|
|
50
|
+
on conflict on constraint callback_endpoints_url_unique do update
|
|
51
|
+
set endpoint_status = 'pending_verification',
|
|
52
|
+
verification_requested_at = now(),
|
|
53
|
+
verified_at = null,
|
|
54
|
+
disabled_at = null,
|
|
55
|
+
disabled_reason = null,
|
|
56
|
+
metadata = coalesce(ce.metadata, '{}'::jsonb) || excluded.metadata,
|
|
57
|
+
updated_at = now()
|
|
58
|
+
returning ce.id, ce.owner_agent_id, ce.endpoint_status, ce.signing_key_version, ce.verification_requested_at, ce.updated_at;
|
|
59
|
+
end;
|
|
60
|
+
$$;
|
|
61
|
+
|
|
62
|
+
create or replace function agent_board.verify_callback_endpoint(
|
|
63
|
+
p_endpoint_id uuid,
|
|
64
|
+
p_owner_agent_id uuid,
|
|
65
|
+
p_metadata jsonb default '{}'::jsonb
|
|
66
|
+
)
|
|
67
|
+
returns table(endpoint_id uuid, owner_agent_id uuid, endpoint_status text, verified_at timestamptz, updated_at timestamptz)
|
|
68
|
+
language plpgsql
|
|
69
|
+
security definer
|
|
70
|
+
set search_path = agent_board, public
|
|
71
|
+
as $$
|
|
72
|
+
declare
|
|
73
|
+
v_endpoint agent_board.callback_endpoints%rowtype;
|
|
74
|
+
v_actor_agent_id uuid;
|
|
75
|
+
v_control_proof_ref text := nullif(trim(coalesce(p_metadata ->> 'controlProofRef', '')), '');
|
|
76
|
+
v_metadata jsonb;
|
|
77
|
+
begin
|
|
78
|
+
v_actor_agent_id := agent_board._resolved_actor_agent_id(p_owner_agent_id, 'p_owner_agent_id');
|
|
79
|
+
v_endpoint := agent_board._owned_callback_endpoint(p_endpoint_id, v_actor_agent_id);
|
|
80
|
+
|
|
81
|
+
if v_control_proof_ref is null then
|
|
82
|
+
raise exception 'callback endpoint verification requires controlProofRef metadata' using errcode = '22023';
|
|
83
|
+
end if;
|
|
84
|
+
|
|
85
|
+
v_metadata := coalesce(v_endpoint.metadata, '{}'::jsonb)
|
|
86
|
+
|| coalesce(p_metadata, '{}'::jsonb)
|
|
87
|
+
|| jsonb_build_object(
|
|
88
|
+
'verificationMethod',
|
|
89
|
+
'administrative_enablement',
|
|
90
|
+
'controlProofRef',
|
|
91
|
+
v_control_proof_ref,
|
|
92
|
+
'controlProofRecordedAt',
|
|
93
|
+
now()
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
update agent_board.callback_endpoints ce
|
|
97
|
+
set endpoint_status = 'verified',
|
|
98
|
+
verified_at = now(),
|
|
99
|
+
disabled_at = null,
|
|
100
|
+
disabled_reason = null,
|
|
101
|
+
metadata = v_metadata,
|
|
102
|
+
updated_at = now()
|
|
103
|
+
where ce.id = v_endpoint.id
|
|
104
|
+
returning * into v_endpoint;
|
|
105
|
+
|
|
106
|
+
return query
|
|
107
|
+
select v_endpoint.id, v_endpoint.owner_agent_id, v_endpoint.endpoint_status, v_endpoint.verified_at, v_endpoint.updated_at;
|
|
108
|
+
end;
|
|
109
|
+
$$;
|
|
110
|
+
|
|
111
|
+
grant execute on function agent_board.register_callback_endpoint(uuid, text, jsonb) to authenticated;
|
|
112
|
+
grant execute on function agent_board.verify_callback_endpoint(uuid, uuid, jsonb) to authenticated;
|
|
113
|
+
grant execute on all functions in schema agent_board to service_role;
|