@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,219 @@
|
|
|
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(13);
|
|
7
|
+
|
|
8
|
+
select has_function('agent_board', 'reserve_signed_write_replay', 'replay reservation helper exists');
|
|
9
|
+
select has_function('agent_board', 'signed_send_direct_message', 'signed direct-message wrapper exists');
|
|
10
|
+
select has_function('agent_board', 'signed_append_task_event', 'signed task-event wrapper exists');
|
|
11
|
+
select has_function('agent_board', 'signed_post_board_message', 'signed board-message wrapper exists');
|
|
12
|
+
|
|
13
|
+
select hasnt_table('agent_board', 'private_signing_keys', 'gateway does not introduce private key storage');
|
|
14
|
+
|
|
15
|
+
do $$
|
|
16
|
+
declare
|
|
17
|
+
v_agent uuid := gen_random_uuid();
|
|
18
|
+
v_recipient uuid := gen_random_uuid();
|
|
19
|
+
v_thread uuid := gen_random_uuid();
|
|
20
|
+
v_task uuid := gen_random_uuid();
|
|
21
|
+
v_board uuid := gen_random_uuid();
|
|
22
|
+
v_key uuid;
|
|
23
|
+
v_direct record;
|
|
24
|
+
v_task_event record;
|
|
25
|
+
v_board_post record;
|
|
26
|
+
begin
|
|
27
|
+
perform set_config('request.jwt.claim.role', 'service_role', true);
|
|
28
|
+
|
|
29
|
+
insert into agent_board.agents(id, agent_type, handle, display_name)
|
|
30
|
+
values
|
|
31
|
+
(v_agent, 'agent', 'signgateway-agent', 'SIGNGATEWAY Agent'),
|
|
32
|
+
(v_recipient, 'agent', 'signgateway-recipient', 'SIGNGATEWAY Recipient');
|
|
33
|
+
|
|
34
|
+
insert into agent_board.threads(id, created_by_agent_id, subject)
|
|
35
|
+
values (v_thread, v_agent, 'SIGNGATEWAY thread');
|
|
36
|
+
|
|
37
|
+
insert into agent_board.tasks(id, thread_id, created_by_agent_id, title)
|
|
38
|
+
values (v_task, v_thread, v_agent, 'SIGNGATEWAY task');
|
|
39
|
+
|
|
40
|
+
insert into agent_board.boards(id, slug, name, created_by_agent_id)
|
|
41
|
+
values (v_board, 'signgateway', 'SIGNGATEWAY', v_agent);
|
|
42
|
+
|
|
43
|
+
insert into agent_board.board_memberships(board_id, agent_id, membership_state, created_by_agent_id)
|
|
44
|
+
values (v_board, v_agent, 'joined', v_agent);
|
|
45
|
+
|
|
46
|
+
insert into agent_board.agent_signing_keys(
|
|
47
|
+
agent_id,
|
|
48
|
+
key_id,
|
|
49
|
+
key_version,
|
|
50
|
+
public_key,
|
|
51
|
+
public_key_format,
|
|
52
|
+
fingerprint,
|
|
53
|
+
status,
|
|
54
|
+
proof_state
|
|
55
|
+
)
|
|
56
|
+
values (
|
|
57
|
+
v_agent,
|
|
58
|
+
'primary',
|
|
59
|
+
1,
|
|
60
|
+
repeat('A', 43),
|
|
61
|
+
'raw_ed25519_base64url',
|
|
62
|
+
'sha256:' || repeat('A', 43),
|
|
63
|
+
'active',
|
|
64
|
+
'proven'
|
|
65
|
+
)
|
|
66
|
+
returning id into v_key;
|
|
67
|
+
|
|
68
|
+
perform agent_board.reserve_signed_write_replay(v_agent, v_key, 'send_direct_message', 'sha256:' || repeat('d', 64), 'nonce-1', null, now(), now() + interval '10 minutes');
|
|
69
|
+
|
|
70
|
+
select *
|
|
71
|
+
into v_direct
|
|
72
|
+
from agent_board.signed_send_direct_message(
|
|
73
|
+
jsonb_build_object(
|
|
74
|
+
'p_thread_id', v_thread,
|
|
75
|
+
'p_author_agent_id', v_agent,
|
|
76
|
+
'p_recipient_agent_id', v_recipient,
|
|
77
|
+
'p_subject', 'Signed direct',
|
|
78
|
+
'p_body', 'verified signature direct message',
|
|
79
|
+
'p_metadata', jsonb_build_object('source', 'test')
|
|
80
|
+
),
|
|
81
|
+
jsonb_build_object(
|
|
82
|
+
'agent_id', v_agent,
|
|
83
|
+
'signing_key_id', v_key,
|
|
84
|
+
'key_id', 'primary',
|
|
85
|
+
'key_version', 1,
|
|
86
|
+
'algorithm', 'Ed25519',
|
|
87
|
+
'key_fingerprint', 'sha256:' || repeat('A', 43),
|
|
88
|
+
'verification_status', 'verified',
|
|
89
|
+
'payload_digest', 'sha256:' || repeat('d', 64),
|
|
90
|
+
'signature_digest', 'sha256:' || repeat('e', 64),
|
|
91
|
+
'canonicalization_version', 'agent-provenance-jcs.v1',
|
|
92
|
+
'signed_at', now(),
|
|
93
|
+
'verified_at', now(),
|
|
94
|
+
'nonce', 'nonce-1',
|
|
95
|
+
'enforcement_mode', 'require_for_agent_writes',
|
|
96
|
+
'redacted', true
|
|
97
|
+
)
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
select *
|
|
101
|
+
into v_task_event
|
|
102
|
+
from agent_board.signed_append_task_event(
|
|
103
|
+
jsonb_build_object(
|
|
104
|
+
'p_task_id', v_task,
|
|
105
|
+
'p_actor_agent_id', v_agent,
|
|
106
|
+
'p_event_type', 'status_changed',
|
|
107
|
+
'p_status', 'completed',
|
|
108
|
+
'p_payload', jsonb_build_object('source', 'test')
|
|
109
|
+
),
|
|
110
|
+
jsonb_build_object(
|
|
111
|
+
'agent_id', v_agent,
|
|
112
|
+
'signing_key_id', v_key,
|
|
113
|
+
'key_id', 'primary',
|
|
114
|
+
'key_version', 1,
|
|
115
|
+
'verification_status', 'verified',
|
|
116
|
+
'payload_digest', 'sha256:' || repeat('f', 64),
|
|
117
|
+
'canonicalization_version', 'agent-provenance-jcs.v1',
|
|
118
|
+
'enforcement_mode', 'require_for_agent_writes',
|
|
119
|
+
'redacted', true
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
select *
|
|
124
|
+
into v_board_post
|
|
125
|
+
from agent_board.signed_post_board_message(
|
|
126
|
+
jsonb_build_object(
|
|
127
|
+
'p_thread_id', v_thread,
|
|
128
|
+
'p_author_agent_id', v_agent,
|
|
129
|
+
'p_board_id', v_board,
|
|
130
|
+
'p_subject', 'Signed board',
|
|
131
|
+
'p_body', 'verified signature board message',
|
|
132
|
+
'p_metadata', jsonb_build_object('source', 'test')
|
|
133
|
+
),
|
|
134
|
+
jsonb_build_object(
|
|
135
|
+
'agent_id', v_agent,
|
|
136
|
+
'signing_key_id', v_key,
|
|
137
|
+
'key_id', 'primary',
|
|
138
|
+
'key_version', 1,
|
|
139
|
+
'verification_status', 'warn',
|
|
140
|
+
'payload_digest', 'sha256:' || repeat('b', 64),
|
|
141
|
+
'canonicalization_version', 'agent-provenance-jcs.v1',
|
|
142
|
+
'enforcement_mode', 'warn',
|
|
143
|
+
'redacted', true
|
|
144
|
+
)
|
|
145
|
+
);
|
|
146
|
+
end
|
|
147
|
+
$$;
|
|
148
|
+
|
|
149
|
+
select ok(
|
|
150
|
+
exists (select 1 from agent_board.signature_evidence where operation = 'send_direct_message' and verification_status = 'verified' and source_message_id is not null),
|
|
151
|
+
'valid signed direct message records linked signature evidence'
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
select ok(
|
|
155
|
+
exists (select 1 from agent_board.signature_evidence where operation = 'append_task_event' and verification_status = 'verified' and source_task_event_id is not null),
|
|
156
|
+
'valid signed task event records linked signature evidence'
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
select ok(
|
|
160
|
+
exists (select 1 from agent_board.signature_evidence where operation = 'post_board_message' and verification_status = 'warn' and metadata ->> 'metadata-safe' = 'true'),
|
|
161
|
+
'warn-mode unsigned compatibility can record metadata-safe evidence'
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
select throws_like(
|
|
165
|
+
$$select * from agent_board.reserve_signed_write_replay(
|
|
166
|
+
(select id from agent_board.agents where handle = 'signgateway-agent'),
|
|
167
|
+
(
|
|
168
|
+
select sk.id
|
|
169
|
+
from agent_board.agent_signing_keys sk
|
|
170
|
+
join agent_board.agents a on a.id = sk.agent_id
|
|
171
|
+
where sk.key_id = 'primary'
|
|
172
|
+
and a.handle = 'signgateway-agent'
|
|
173
|
+
),
|
|
174
|
+
'send_direct_message',
|
|
175
|
+
'sha256:' || repeat('d', 64),
|
|
176
|
+
'nonce-1',
|
|
177
|
+
null,
|
|
178
|
+
now(),
|
|
179
|
+
now() + interval '10 minutes'
|
|
180
|
+
)$$,
|
|
181
|
+
'%nonce_replay%',
|
|
182
|
+
'replayed nonce is rejected'
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
select throws_like(
|
|
186
|
+
$$select * from agent_board.signed_send_direct_message(
|
|
187
|
+
jsonb_build_object('p_thread_id', gen_random_uuid(), 'p_recipient_agent_id', gen_random_uuid(), 'p_subject', 'x', 'p_body', 'y'),
|
|
188
|
+
jsonb_build_object('verification_status', 'failed', 'failure_class', 'actor_mismatch')
|
|
189
|
+
)$$,
|
|
190
|
+
'%actor_mismatch%',
|
|
191
|
+
'actor mismatch fails before canonical write'
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
select ok(
|
|
195
|
+
exists (
|
|
196
|
+
select 1 from agent_board.signature_evidence
|
|
197
|
+
where failure_class is null
|
|
198
|
+
and metadata ->> 'redacted' = 'true'
|
|
199
|
+
),
|
|
200
|
+
'signature evidence records redaction posture'
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
select ok(
|
|
204
|
+
not exists (
|
|
205
|
+
select 1
|
|
206
|
+
from agent_board.signature_evidence
|
|
207
|
+
where metadata::text ~* 'private key|service-role|secret|raw signature'
|
|
208
|
+
),
|
|
209
|
+
'metadata-safe evidence does not store raw private, service-role, secret, or raw signature text'
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
select ok(
|
|
213
|
+
exists (select 1 from agent_board.schema_version where version >= 5),
|
|
214
|
+
'schema_version preserves SQL-ahead SIGNSTORE compatibility version'
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
select finish();
|
|
218
|
+
|
|
219
|
+
rollback;
|
|
@@ -0,0 +1,143 @@
|
|
|
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(7);
|
|
7
|
+
|
|
8
|
+
select has_function('agent_board', 'issue_signing_key_challenge', array['uuid', 'text', 'interval'], 'issue_signing_key_challenge exists');
|
|
9
|
+
select has_function('agent_board', 'record_signing_key_proof_result', array['uuid', 'uuid', 'text', 'boolean', 'text', 'jsonb'], 'record_signing_key_proof_result exists');
|
|
10
|
+
|
|
11
|
+
do $$
|
|
12
|
+
declare
|
|
13
|
+
v_agent uuid := gen_random_uuid();
|
|
14
|
+
v_challenge record;
|
|
15
|
+
v_result record;
|
|
16
|
+
begin
|
|
17
|
+
insert into agent_board.agents(id, agent_type, handle, display_name)
|
|
18
|
+
values (v_agent, 'agent', 'signproof-agent', 'SIGNPROOF Agent');
|
|
19
|
+
|
|
20
|
+
insert into agent_board.agent_signing_keys(
|
|
21
|
+
agent_id,
|
|
22
|
+
key_id,
|
|
23
|
+
key_version,
|
|
24
|
+
public_key,
|
|
25
|
+
public_key_format,
|
|
26
|
+
fingerprint
|
|
27
|
+
)
|
|
28
|
+
values (
|
|
29
|
+
v_agent,
|
|
30
|
+
'primary',
|
|
31
|
+
1,
|
|
32
|
+
repeat('A', 43),
|
|
33
|
+
'raw_ed25519_base64url',
|
|
34
|
+
'sha256:' || repeat('Z', 43)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
set local role service_role;
|
|
38
|
+
select * into v_challenge
|
|
39
|
+
from agent_board.issue_signing_key_challenge(v_agent, 'primary', interval '5 minutes');
|
|
40
|
+
|
|
41
|
+
if v_challenge.challenge !~ '^agent-signing-key-proof\.v1:' then
|
|
42
|
+
raise exception 'challenge bytes did not use proof prefix';
|
|
43
|
+
end if;
|
|
44
|
+
|
|
45
|
+
select * into v_result
|
|
46
|
+
from agent_board.record_signing_key_proof_result(
|
|
47
|
+
v_challenge.challenge_id,
|
|
48
|
+
v_agent,
|
|
49
|
+
'primary',
|
|
50
|
+
true,
|
|
51
|
+
null,
|
|
52
|
+
'{"proofRuntime":"vitest"}'::jsonb
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if v_result.proof_state <> 'proven' or v_result.verified_at is null then
|
|
56
|
+
raise exception 'verified proof did not activate proof state';
|
|
57
|
+
end if;
|
|
58
|
+
|
|
59
|
+
begin
|
|
60
|
+
perform *
|
|
61
|
+
from agent_board.record_signing_key_proof_result(
|
|
62
|
+
v_challenge.challenge_id,
|
|
63
|
+
v_agent,
|
|
64
|
+
'primary',
|
|
65
|
+
true,
|
|
66
|
+
null,
|
|
67
|
+
'{}'::jsonb
|
|
68
|
+
);
|
|
69
|
+
raise exception 'challenge replay unexpectedly succeeded';
|
|
70
|
+
exception
|
|
71
|
+
when unique_violation then null;
|
|
72
|
+
end;
|
|
73
|
+
end
|
|
74
|
+
$$;
|
|
75
|
+
|
|
76
|
+
reset role;
|
|
77
|
+
|
|
78
|
+
select pass('challenge issuance records bounded challenge bytes');
|
|
79
|
+
select pass('verified proof activates the signing key');
|
|
80
|
+
select pass('challenge replay is rejected');
|
|
81
|
+
|
|
82
|
+
do $$
|
|
83
|
+
declare
|
|
84
|
+
v_agent uuid := gen_random_uuid();
|
|
85
|
+
v_challenge record;
|
|
86
|
+
v_result record;
|
|
87
|
+
begin
|
|
88
|
+
insert into agent_board.agents(id, agent_type, handle, display_name)
|
|
89
|
+
values (v_agent, 'agent', 'signproof-fail-agent', 'SIGNPROOF Fail Agent');
|
|
90
|
+
|
|
91
|
+
insert into agent_board.agent_signing_keys(
|
|
92
|
+
agent_id,
|
|
93
|
+
key_id,
|
|
94
|
+
key_version,
|
|
95
|
+
public_key,
|
|
96
|
+
public_key_format,
|
|
97
|
+
fingerprint
|
|
98
|
+
)
|
|
99
|
+
values (
|
|
100
|
+
v_agent,
|
|
101
|
+
'primary',
|
|
102
|
+
1,
|
|
103
|
+
repeat('B', 43),
|
|
104
|
+
'raw_ed25519_base64url',
|
|
105
|
+
'sha256:' || repeat('Y', 43)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
set local role service_role;
|
|
109
|
+
select * into v_challenge
|
|
110
|
+
from agent_board.issue_signing_key_challenge(v_agent, 'primary', interval '5 minutes');
|
|
111
|
+
|
|
112
|
+
select * into v_result
|
|
113
|
+
from agent_board.record_signing_key_proof_result(
|
|
114
|
+
v_challenge.challenge_id,
|
|
115
|
+
v_agent,
|
|
116
|
+
'primary',
|
|
117
|
+
false,
|
|
118
|
+
'signature_invalid',
|
|
119
|
+
'{}'::jsonb
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if v_result.proof_state <> 'failed' or v_result.failure_class <> 'signature_invalid' then
|
|
123
|
+
raise exception 'failed proof did not preserve signature_invalid failure class';
|
|
124
|
+
end if;
|
|
125
|
+
end
|
|
126
|
+
$$;
|
|
127
|
+
|
|
128
|
+
select pass('failed proof preserves metadata-safe failure class');
|
|
129
|
+
|
|
130
|
+
select ok(
|
|
131
|
+
not exists (
|
|
132
|
+
select 1
|
|
133
|
+
from information_schema.routine_privileges
|
|
134
|
+
where specific_schema = 'agent_board'
|
|
135
|
+
and routine_name in ('issue_signing_key_challenge', 'record_signing_key_proof_result')
|
|
136
|
+
and grantee = 'anon'
|
|
137
|
+
),
|
|
138
|
+
'proof RPCs are not executable by anon'
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
select finish();
|
|
142
|
+
|
|
143
|
+
rollback;
|
|
@@ -0,0 +1,183 @@
|
|
|
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(16);
|
|
7
|
+
|
|
8
|
+
select has_table('agent_board', 'agent_signing_keys', 'agent_signing_keys exists');
|
|
9
|
+
select has_table('agent_board', 'agent_signing_key_challenges', 'agent_signing_key_challenges exists');
|
|
10
|
+
select has_table('agent_board', 'signature_evidence', 'signature_evidence exists');
|
|
11
|
+
select has_table('agent_board', 'signature_replay_state', 'signature_replay_state exists');
|
|
12
|
+
|
|
13
|
+
select has_column('agent_board', 'agent_signing_keys', 'key_id', 'key_id is stored');
|
|
14
|
+
select has_column('agent_board', 'agent_signing_keys', 'key_version', 'key_version is stored');
|
|
15
|
+
select has_column('agent_board', 'agent_signing_keys', 'fingerprint', 'fingerprint is stored');
|
|
16
|
+
select col_has_check('agent_board', 'agent_signing_keys', 'algorithm', 'algorithm is constrained');
|
|
17
|
+
select col_has_check('agent_board', 'agent_signing_keys', 'public_key_format', 'public key format is constrained');
|
|
18
|
+
select col_has_check('agent_board', 'agent_signing_keys', 'status', 'status is constrained');
|
|
19
|
+
select col_has_check('agent_board', 'agent_signing_keys', 'proof_state', 'proof state is constrained');
|
|
20
|
+
select has_index('agent_board', 'agent_signing_keys', 'agent_signing_keys_key_id_unique', 'key_id is unique per agent');
|
|
21
|
+
select has_index('agent_board', 'agent_signing_keys', 'agent_signing_keys_key_version_unique', 'key_version is unique per agent');
|
|
22
|
+
|
|
23
|
+
do $$
|
|
24
|
+
declare
|
|
25
|
+
v_agent uuid := gen_random_uuid();
|
|
26
|
+
v_message uuid := gen_random_uuid();
|
|
27
|
+
v_thread uuid := gen_random_uuid();
|
|
28
|
+
v_key uuid;
|
|
29
|
+
begin
|
|
30
|
+
insert into agent_board.agents(id, agent_type, handle, display_name)
|
|
31
|
+
values (v_agent, 'agent', 'signstore-agent', 'SIGNSTORE Agent');
|
|
32
|
+
|
|
33
|
+
insert into agent_board.threads(id, created_by_agent_id, subject)
|
|
34
|
+
values (v_thread, v_agent, 'SIGNSTORE thread');
|
|
35
|
+
|
|
36
|
+
insert into agent_board.messages(id, thread_id, author_agent_id, message_sequence, body)
|
|
37
|
+
values (v_message, v_thread, v_agent, 1, 'signed message');
|
|
38
|
+
|
|
39
|
+
insert into agent_board.agent_signing_keys(
|
|
40
|
+
agent_id,
|
|
41
|
+
key_id,
|
|
42
|
+
key_version,
|
|
43
|
+
public_key,
|
|
44
|
+
public_key_format,
|
|
45
|
+
fingerprint,
|
|
46
|
+
status,
|
|
47
|
+
proof_state
|
|
48
|
+
)
|
|
49
|
+
values (
|
|
50
|
+
v_agent,
|
|
51
|
+
'primary',
|
|
52
|
+
1,
|
|
53
|
+
repeat('A', 43),
|
|
54
|
+
'raw_ed25519_base64url',
|
|
55
|
+
'sha256:' || repeat('A', 43),
|
|
56
|
+
'active',
|
|
57
|
+
'proven'
|
|
58
|
+
)
|
|
59
|
+
returning id into v_key;
|
|
60
|
+
|
|
61
|
+
begin
|
|
62
|
+
insert into agent_board.agent_signing_keys(
|
|
63
|
+
agent_id,
|
|
64
|
+
key_id,
|
|
65
|
+
key_version,
|
|
66
|
+
public_key,
|
|
67
|
+
public_key_format,
|
|
68
|
+
fingerprint
|
|
69
|
+
)
|
|
70
|
+
values (
|
|
71
|
+
v_agent,
|
|
72
|
+
'secondary',
|
|
73
|
+
1,
|
|
74
|
+
repeat('B', 43),
|
|
75
|
+
'raw_ed25519_base64url',
|
|
76
|
+
'sha256:' || repeat('B', 43)
|
|
77
|
+
);
|
|
78
|
+
raise exception 'duplicate key_version unexpectedly succeeded';
|
|
79
|
+
exception
|
|
80
|
+
when unique_violation then null;
|
|
81
|
+
end;
|
|
82
|
+
|
|
83
|
+
begin
|
|
84
|
+
insert into agent_board.agent_signing_keys(
|
|
85
|
+
agent_id,
|
|
86
|
+
key_id,
|
|
87
|
+
key_version,
|
|
88
|
+
public_key,
|
|
89
|
+
public_key_format,
|
|
90
|
+
fingerprint,
|
|
91
|
+
status,
|
|
92
|
+
proof_state
|
|
93
|
+
)
|
|
94
|
+
values (
|
|
95
|
+
v_agent,
|
|
96
|
+
'unproven-active',
|
|
97
|
+
2,
|
|
98
|
+
repeat('C', 43),
|
|
99
|
+
'raw_ed25519_base64url',
|
|
100
|
+
'sha256:' || repeat('C', 43),
|
|
101
|
+
'active',
|
|
102
|
+
'unproven'
|
|
103
|
+
);
|
|
104
|
+
raise exception 'active key without proof unexpectedly succeeded';
|
|
105
|
+
exception
|
|
106
|
+
when check_violation then null;
|
|
107
|
+
end;
|
|
108
|
+
|
|
109
|
+
insert into agent_board.signature_evidence(
|
|
110
|
+
agent_id,
|
|
111
|
+
signing_key_id,
|
|
112
|
+
key_id,
|
|
113
|
+
key_version,
|
|
114
|
+
source_kind,
|
|
115
|
+
source_message_id,
|
|
116
|
+
operation,
|
|
117
|
+
payload_digest,
|
|
118
|
+
signature_digest,
|
|
119
|
+
verification_status,
|
|
120
|
+
verified_at,
|
|
121
|
+
nonce
|
|
122
|
+
)
|
|
123
|
+
values (
|
|
124
|
+
v_agent,
|
|
125
|
+
v_key,
|
|
126
|
+
'primary',
|
|
127
|
+
1,
|
|
128
|
+
'message',
|
|
129
|
+
v_message,
|
|
130
|
+
'agent_board.post_message',
|
|
131
|
+
'sha256:' || repeat('d', 64),
|
|
132
|
+
'sha256:' || repeat('e', 64),
|
|
133
|
+
'verified',
|
|
134
|
+
now(),
|
|
135
|
+
'nonce-1'
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
insert into agent_board.signature_replay_state(
|
|
139
|
+
agent_id,
|
|
140
|
+
signing_key_id,
|
|
141
|
+
operation,
|
|
142
|
+
payload_digest,
|
|
143
|
+
nonce,
|
|
144
|
+
envelope_timestamp,
|
|
145
|
+
expires_at
|
|
146
|
+
)
|
|
147
|
+
values (
|
|
148
|
+
v_agent,
|
|
149
|
+
v_key,
|
|
150
|
+
'agent_board.post_message',
|
|
151
|
+
'sha256:' || repeat('d', 64),
|
|
152
|
+
'nonce-1',
|
|
153
|
+
now(),
|
|
154
|
+
now() + interval '10 minutes'
|
|
155
|
+
);
|
|
156
|
+
end
|
|
157
|
+
$$;
|
|
158
|
+
|
|
159
|
+
select pass('signing key uniqueness, proof-state, signature evidence, and replay metadata checks passed');
|
|
160
|
+
|
|
161
|
+
select ok(
|
|
162
|
+
exists (
|
|
163
|
+
select 1
|
|
164
|
+
from agent_board.schema_version
|
|
165
|
+
where version >= 5
|
|
166
|
+
),
|
|
167
|
+
'schema_version advanced to SQL-ahead SIGNSTORE version'
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
select ok(
|
|
171
|
+
not exists (
|
|
172
|
+
select 1
|
|
173
|
+
from information_schema.columns
|
|
174
|
+
where table_schema = 'agent_board'
|
|
175
|
+
and table_name in ('agent_signing_keys', 'signature_evidence')
|
|
176
|
+
and column_name ~* 'private|secret|token'
|
|
177
|
+
),
|
|
178
|
+
'SIGNSTORE tables do not expose private key, secret, or token columns'
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
select finish();
|
|
182
|
+
|
|
183
|
+
rollback;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
SELECT plan(8);
|
|
2
|
+
|
|
3
|
+
SELECT has_schema('agent_board', 'agent_board schema exists');
|
|
4
|
+
SELECT has_table('agent_board', 'agents', 'agents table exists');
|
|
5
|
+
SELECT has_table('agent_board', 'tasks', 'tasks table exists');
|
|
6
|
+
SELECT has_table('agent_board', 'claims', 'claims table exists');
|
|
7
|
+
SELECT has_view('agent_board', 'v_thread_feed', 'thread feed view exists');
|
|
8
|
+
SELECT has_view('agent_board', 'v_task_tree', 'task tree view exists');
|
|
9
|
+
SELECT has_view('agent_board', 'v_active_claims', 'active claims view exists');
|
|
10
|
+
SELECT has_function('agent_board', 'acquire_claim', ARRAY['uuid', 'text', 'text', 'timestamp with time zone', 'uuid', 'uuid', 'jsonb'], 'acquire_claim rpc exists');
|
|
11
|
+
|
|
12
|
+
SELECT finish();
|