@consiliency/agent-board-schema 1.59.0 → 1.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +80 -0
  2. package/fixtures/already-applied.json +16 -0
  3. package/migrations/20261010000100_agent_board_own_post_not_delivered.sql +637 -0
  4. package/migrations/20261011000100_agent_board_session_identities.sql +1964 -0
  5. package/migrations/20261012000100_agent_board_sidowner.sql +209 -0
  6. package/migrations/20261014000100_agent_board_grant_target_role.sql +218 -0
  7. package/package.json +1 -1
  8. package/schema-manifest.json +39 -3
  9. package/src/tooling.test.mjs +9 -2
  10. package/tests/agent_board_addressing.test.sql +3 -1
  11. package/tests/agent_board_atomic_fleet_onboarding.test.sql +13 -4
  12. package/tests/agent_board_consent_emission.test.sql +30 -11
  13. package/tests/agent_board_consent_gating.test.sql +16 -2
  14. package/tests/agent_board_delegate_role_class.test.sql +1 -1
  15. package/tests/agent_board_delegation_cap_ceiling.test.sql +26 -15
  16. package/tests/agent_board_delegation_enforcement.test.sql +1 -1
  17. package/tests/agent_board_enroll_resumption.test.sql +22 -4
  18. package/tests/agent_board_enroll_token_single_use.test.sql +18 -3
  19. package/tests/agent_board_federated_lock_order_class.test.sql +2 -2
  20. package/tests/agent_board_fk_lock_edges.test.sql +74 -14
  21. package/tests/agent_board_grant_target_role.test.sql +1118 -0
  22. package/tests/agent_board_inbox_rpcs.test.sql +5 -2
  23. package/tests/agent_board_invite_no_downgrade.test.sql +10 -8
  24. package/tests/agent_board_ledger_authority.test.sql +70 -12
  25. package/tests/agent_board_membership_current_row_class.test.sql +9 -1
  26. package/tests/agent_board_mint_fleet_agent_session.test.sql +2 -2
  27. package/tests/agent_board_mint_harden.test.sql +38 -27
  28. package/tests/agent_board_own_post_not_delivered.test.sql +490 -0
  29. package/tests/agent_board_rolereset.test.sql +8 -2
  30. package/tests/agent_board_roomdeleg_lane_b.test.sql +6 -6
  31. package/tests/agent_board_rowid.test.sql +3 -1
  32. package/tests/agent_board_session_identity_approval_revocation.test.sql +334 -0
  33. package/tests/agent_board_session_identity_backfill.test.sql +82 -0
  34. package/tests/agent_board_session_identity_backfill_refusal.test.sql +14 -0
  35. package/tests/agent_board_session_identity_behavior_classes.test.sql +176 -0
  36. package/tests/agent_board_session_identity_class.test.sql +118 -0
  37. package/tests/agent_board_session_identity_fingerprint.test.sql +271 -0
  38. package/tests/agent_board_session_identity_labels_limits.test.sql +205 -0
  39. package/tests/agent_board_session_identity_lease.test.sql +235 -0
  40. package/tests/agent_board_session_identity_lifetime.test.sql +230 -0
  41. package/tests/agent_board_session_identity_limits.test.sql +208 -0
  42. package/tests/agent_board_session_identity_lineage.test.sql +64 -0
  43. package/tests/agent_board_session_identity_link.test.sql +201 -0
  44. package/tests/agent_board_session_identity_mint.test.sql +233 -0
  45. package/tests/agent_board_session_identity_panel_r1.test.sql +194 -0
  46. package/tests/agent_board_session_identity_panel_r2.test.sql +174 -0
  47. package/tests/agent_board_session_identity_protected_actions.test.sql +156 -0
  48. package/tests/agent_board_session_identity_revoke_closure.test.sql +131 -0
  49. package/tests/agent_board_sidowner_leases.test.sql +107 -0
  50. package/tests/agent_board_sidowner_owner_required.test.sql +97 -0
  51. package/tests/agent_board_sidowner_read_class.test.sql +171 -0
  52. package/tests/agent_board_sidowner_reads.test.sql +136 -0
  53. package/tests/agent_board_signer_lifecycle.test.sql +26 -4
  54. package/tests/agent_board_signing_challenge_pending_cap.test.sql +1 -1
  55. package/tests/agent_board_v2_invariants.test.sql +4 -4
  56. package/tests/agent_board_write_enforcement_class.test.sql +74 -15
  57. package/tests/catalog/function_execute_privileges.json +218 -2
  58. package/tests/catalog/p_metadata_parameters.json +98 -2
  59. package/tests/isolation/expected/grant_target_role_race.out +348 -0
  60. package/tests/isolation/expected/session_identity_limits.out +165 -0
  61. package/tests/isolation/expected/session_identity_links.out +235 -0
  62. package/tests/isolation/expected/session_identity_reclaim.out +253 -0
  63. package/tests/isolation/expected/session_identity_renew_revoke.out +115 -0
  64. package/tests/isolation/expected/session_identity_renew_revoke_key_gate.out +138 -0
  65. package/tests/isolation/expected/session_identity_tombstone_onboarding.out +247 -0
  66. package/tests/isolation/specs/grant_target_role_race.spec +207 -0
  67. package/tests/isolation/specs/session_identity_limits.spec +81 -0
  68. package/tests/isolation/specs/session_identity_links.spec +80 -0
  69. package/tests/isolation/specs/session_identity_reclaim.spec +125 -0
  70. package/tests/isolation/specs/session_identity_renew_revoke.spec +84 -0
  71. package/tests/isolation/specs/session_identity_renew_revoke_key_gate.spec +94 -0
  72. package/tests/isolation/specs/session_identity_tombstone_onboarding.spec +108 -0
  73. package/tests/seeds/20261011000100_agent_board_session_identities.sql.pre.sql +47 -0
@@ -0,0 +1,94 @@
1
+ # A third session holds only the signing key. Revoke acquires root/identity
2
+ # then waits for that key, pinning renewal blocking BEFORE its state reread.
3
+ # Renewal and owner revocation of a real minted/claimed identity, both commit
4
+ # orders. The loser waits on the identity row; revoke-after-renew fences its
5
+ # result, and renew-after-revoke must re-read the tombstone rather than revive it.
6
+ setup
7
+ {
8
+ grant usage on schema extensions to service_role, authenticated;
9
+ insert into auth.users(id) values ('81000000-0000-4000-8000-000000000001');
10
+ insert into agent_board.agents(id, agent_type, handle, display_name, status, auth_user_id, owner_user_id)
11
+ values ('82000000-0000-4000-8000-000000000001', 'human', 'sid-renew-owner', 'Renew owner', 'active',
12
+ '81000000-0000-4000-8000-000000000001', null),
13
+ ('82000000-0000-4000-8000-000000000002', 'agent', 'sid-renew-root', 'Renew root', 'active', null,
14
+ '81000000-0000-4000-8000-000000000001');
15
+ insert into agent_board.session_identities(agent_id, root_agent_id, depth, current_risky_digest, approved_risky_digest)
16
+ values ('82000000-0000-4000-8000-000000000002', '82000000-0000-4000-8000-000000000002', 0, 'approved', 'approved');
17
+ BEGIN;
18
+ SET LOCAL ROLE service_role;
19
+ SET LOCAL "request.jwt.claim.role" = 'service_role';
20
+ select count(*) from agent_board.upsert_delegation_policy('82000000-0000-4000-8000-000000000002', 'root_minted', '[]', 200, interval '7 days', true);
21
+ do $$ declare m record; c record; begin
22
+ select * into m from agent_board.mint_delegated_child('82000000-0000-4000-8000-000000000002', 'sid-renew-child', '[]',
23
+ public.test_pubkey('sid-renew-child'), interval '1 hour', false, 'claude', 'renew-original', null, '[]',
24
+ jsonb_build_object('resolution', 'resolved', 'risky_digest', 'approved'));
25
+ select * into c from agent_board.issue_signing_key_challenge(m.child_agent_id, 'delegated-ephemeral');
26
+ perform * from agent_board.mint_fleet_agent_session(c.challenge_id, split_part(c.challenge, ':', 3),
27
+ m.signing_key_id, m.child_agent_id, 900, null, 'renew-original');
28
+ end $$;
29
+ COMMIT;
30
+ }
31
+
32
+ teardown
33
+ {
34
+ do $$ declare v_child uuid; v_auth uuid; begin
35
+ select id, auth_user_id into v_child, v_auth from agent_board.agents where handle = 'sid-renew-child';
36
+ update agent_board.session_identities set lease_alias_id = null where agent_id = v_child;
37
+ delete from agent_board.session_id_aliases where identity_agent_id = v_child;
38
+ delete from agent_board.session_identities where agent_id = v_child;
39
+ delete from agent_board.nonfleet_sessions where agent_id = v_child;
40
+ delete from agent_board.agents where id = v_child;
41
+ delete from auth.users where id = v_auth;
42
+ delete from agent_board.session_identities where agent_id = '82000000-0000-4000-8000-000000000002';
43
+ delete from agent_board.agents where id in ('82000000-0000-4000-8000-000000000001', '82000000-0000-4000-8000-000000000002');
44
+ delete from auth.users where id = '81000000-0000-4000-8000-000000000001';
45
+ end $$;
46
+ }
47
+
48
+ session "renew"
49
+ step "renew_begin" {
50
+ BEGIN;
51
+ select set_config('request.jwt.claim.sub', (select auth_user_id::text from agent_board.agents where handle = 'sid-renew-child'), true) is not null as holder_forwarded;
52
+ SET LOCAL ROLE service_role;
53
+ SET LOCAL "request.jwt.claim.role" = 'service_role';
54
+ SET LOCAL "request.jwt.claim.identity_lease_epoch" = '0';
55
+ }
56
+ step "renew" {
57
+ select agent_board.renew_session_identity((select id from agent_board.agents where handle = 'sid-renew-child'), 'approved');
58
+ }
59
+ step "renew_commit" { COMMIT; }
60
+
61
+ session "revoke"
62
+ step "revoke_begin" {
63
+ BEGIN;
64
+ SET LOCAL ROLE authenticated;
65
+ SET LOCAL "request.jwt.claim.role" = 'authenticated';
66
+ SET LOCAL "request.jwt.claim.sub" = '81000000-0000-4000-8000-000000000001';
67
+ }
68
+ step "revoke" {
69
+ select agent_board.revoke_session_identity((select id from agent_board.agents where handle = 'sid-renew-child'));
70
+ }
71
+ step "revoke_commit" { COMMIT; }
72
+
73
+ session "check"
74
+ step "check" {
75
+ select si.tombstone_reason, si.credential_epoch, a.status, g.revoked_at is not null as grant_revoked,
76
+ g.last_renewed_at is not null as renewal_preceded_revoke,
77
+ (select count(*) from agent_board.fleet_revocation_events e where e.subject_id = a.id) as permanent_revocations,
78
+ (select count(*) from agent_board.agent_signing_keys k where k.agent_id = a.id and k.status <> 'revoked') as active_keys
79
+ from agent_board.agents a join agent_board.session_identities si on si.agent_id = a.id
80
+ join agent_board.delegation_grants g on g.child_agent_id = a.id where a.handle = 'sid-renew-child';
81
+ select bool_and(expires_at <= issued_at + interval '900 seconds') as bounded_pre_revoke_tokens
82
+ from agent_board.nonfleet_sessions where agent_id = (select id from agent_board.agents where handle = 'sid-renew-child');
83
+ }
84
+
85
+ session "key_gate"
86
+ step "key_begin" { BEGIN; }
87
+ step "key_hold" {
88
+ select 1 as held from agent_board.agent_signing_keys
89
+ where agent_id = (select id from agent_board.agents where handle = 'sid-renew-child') for update;
90
+ }
91
+ step "key_commit" { COMMIT; }
92
+
93
+ permutation "key_begin" "key_hold" "revoke_begin" "revoke" "renew_begin" "renew" "key_commit" "revoke_commit" "renew_commit" "check"
94
+ permutation "key_begin" "key_hold" "renew_begin" "renew" "revoke_begin" "revoke" "key_commit" "renew_commit" "revoke_commit" "check"
@@ -0,0 +1,108 @@
1
+ # SIDSCHEMA integrity repair: real verified instance withdrawal, with a real
2
+ # minted session descendant, against onboarding's cycle-relevant acquisitions.
3
+ # The onboarding session is a lock-equivalent stand-in (the established
4
+ # signed_grant_onboarding_deadlock.spec pattern), NOT a full onboarding replay.
5
+ # It takes the actual board mutex, agents FOR UPDATE and memberships FOR UPDATE
6
+ # in onboard_federated_fleet_agent's installed order and modes. Invite/bounds
7
+ # locks before that mutex cannot cycle: withdrawal never accesses those rows.
8
+ # All other onboarding locks follow the mutex; omission cannot create or hide a
9
+ # shared-row cycle while withdrawal waits before its owner/instance/family rows.
10
+ # No timing sleeps. Both orders and the mutex-alone interleaving must complete.
11
+ # The separately mutated catalog class names withdrawal if its mutex is absent;
12
+ # the order pin rejects a mutex moved after the instance. Withdrawal is NOT an
13
+ # exemption based on the membership state it writes.
14
+
15
+ setup
16
+ {
17
+ grant usage on schema extensions to service_role;
18
+ insert into agent_board.agents(id,agent_type,handle,display_name,status,owner_user_id,is_delegate)
19
+ values ('85000000-0000-4000-8000-000000000001','agent','sid-ti-root','Integrity delegate','active',
20
+ agent_board._ensure_agent_auth_user(agent_board._federated_identity_slug('urn:sid-ti:issuer','sid-ti-owner')),true);
21
+ insert into agent_board.session_identities(agent_id,root_agent_id,depth,current_risky_digest,approved_risky_digest)
22
+ values ('85000000-0000-4000-8000-000000000001','85000000-0000-4000-8000-000000000001',0,'approved','approved');
23
+ insert into agent_board.delegate_instances(instance_id,owner_user_id,owner_issuer,owner_subject,agent_id)
24
+ select 'sid_ti_instance_00001',owner_user_id,'urn:sid-ti:issuer','sid-ti-owner',id
25
+ from agent_board.agents where id='85000000-0000-4000-8000-000000000001';
26
+ insert into agent_board.boards(id,slug,name,created_by_agent_id,visibility,status)
27
+ values ('85000000-0000-4000-8000-000000000002','sid-ti-board','Integrity board',
28
+ '85000000-0000-4000-8000-000000000001','private','active');
29
+ insert into agent_board.board_memberships(board_id,agent_id,role,membership_state,created_by_agent_id)
30
+ values ('85000000-0000-4000-8000-000000000002','85000000-0000-4000-8000-000000000001',
31
+ 'member','joined','85000000-0000-4000-8000-000000000001');
32
+ BEGIN;
33
+ SET LOCAL ROLE service_role;
34
+ SET LOCAL "request.jwt.claim.role" = 'service_role';
35
+ select count(*) from agent_board.upsert_delegation_policy('85000000-0000-4000-8000-000000000001',
36
+ 'root_minted','["read"]',200,interval '7 days',true);
37
+ do $$ declare m record; begin
38
+ select * into m from agent_board.mint_delegated_child('85000000-0000-4000-8000-000000000001',
39
+ 'sid-ti-child','["read"]',public.test_pubkey('sid-ti-child'),interval '1 hour',false,
40
+ 'claude','sid-ti-session',null,'[]',jsonb_build_object('resolution','resolved','risky_digest','approved'));
41
+ perform * from agent_board._issue_agent_credential(m.child_agent_id,'sid-ti-fixture');
42
+ end $$;
43
+ COMMIT;
44
+ }
45
+
46
+ teardown
47
+ {
48
+ do $$ declare v_auth_ids uuid[]; begin
49
+ select array_agg(auth_user_id) into v_auth_ids from agent_board.agents
50
+ where root_agent_id='85000000-0000-4000-8000-000000000001' and auth_user_id is not null;
51
+ v_auth_ids := array_append(v_auth_ids,(select owner_user_id from agent_board.agents
52
+ where id='85000000-0000-4000-8000-000000000001'));
53
+ delete from agent_board.fleet_revocation_events where revoked_by_agent_id='85000000-0000-4000-8000-000000000001';
54
+ delete from agent_board.board_membership_events where board_id='85000000-0000-4000-8000-000000000002';
55
+ delete from agent_board.board_memberships where board_id='85000000-0000-4000-8000-000000000002';
56
+ delete from agent_board.boards where id='85000000-0000-4000-8000-000000000002';
57
+ delete from agent_board.delegate_instances where instance_id='sid_ti_instance_00001';
58
+ delete from agent_board.session_id_aliases where root_agent_id='85000000-0000-4000-8000-000000000001';
59
+ delete from agent_board.session_identities where root_agent_id='85000000-0000-4000-8000-000000000001';
60
+ delete from agent_board.agents where root_agent_id='85000000-0000-4000-8000-000000000001';
61
+ delete from agent_board.agents where id='85000000-0000-4000-8000-000000000001';
62
+ delete from auth.users where id=any(v_auth_ids);
63
+ end $$;
64
+ }
65
+
66
+ session "tomb"
67
+ step "t_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
68
+ step "t_tomb" {
69
+ select first from agent_board.tombstone_delegate_instance(
70
+ 'sid_ti_instance_00001','urn:sid-ti:issuer','sid-ti-owner','owner_revoked');
71
+ }
72
+ step "t_commit" { COMMIT; }
73
+
74
+ session "onboard"
75
+ step "o_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
76
+ step "o_advisory" {
77
+ select agent_board._lock_board_authority_ceiling('85000000-0000-4000-8000-000000000002') is null as advisory_taken;
78
+ }
79
+ step "o_agents" {
80
+ select count(*)::int as locked_agents from (
81
+ select 1 from agent_board.agents where id='85000000-0000-4000-8000-000000000001' for update) s;
82
+ }
83
+ step "o_membership" {
84
+ select count(*)::int as locked_memberships from (
85
+ select 1 from agent_board.board_memberships
86
+ where board_id='85000000-0000-4000-8000-000000000002'
87
+ and agent_id='85000000-0000-4000-8000-000000000001' for update) s;
88
+ }
89
+ step "o_commit" { COMMIT; }
90
+
91
+ session "check"
92
+ step "check" {
93
+ select tombstoned_at is not null as instance_tombstoned from agent_board.delegate_instances
94
+ where instance_id='sid_ti_instance_00001';
95
+ select status from agent_board.agents where id='85000000-0000-4000-8000-000000000001';
96
+ select membership_state from agent_board.board_memberships
97
+ where board_id='85000000-0000-4000-8000-000000000002'
98
+ and agent_id='85000000-0000-4000-8000-000000000001';
99
+ select si.tombstoned_at is not null as child_tombstoned,si.credential_epoch,
100
+ (select count(*) from agent_board.agent_credentials ac where ac.agent_id=si.agent_id and ac.revoked_at is null) as live_credentials
101
+ from agent_board.session_identities si join agent_board.agents a on a.id=si.agent_id where a.handle='sid-ti-child';
102
+ select count(*) as withdrawal_events from agent_board.board_membership_events
103
+ where board_id='85000000-0000-4000-8000-000000000002' and event_kind='operator_removal';
104
+ }
105
+
106
+ permutation "o_begin" "o_advisory" "o_agents" "t_begin" "t_tomb" "o_membership" "o_commit" "t_commit" "check"
107
+ permutation "t_begin" "t_tomb" "o_begin" "o_advisory" "t_commit" "o_agents" "o_membership" "o_commit" "check"
108
+ permutation "o_begin" "o_advisory" "t_begin" "t_tomb" "o_agents" "o_membership" "o_commit" "t_commit" "check"
@@ -0,0 +1,47 @@
1
+ -- SIDSCHEMA backfill fixture. These principals exist before the durable
2
+ -- session-identity tables and must retain their original lineage afterwards.
3
+ insert into auth.users(id) values ('61100000-0000-0000-0000-000000000010');
4
+ insert into auth.users(id) values ('61100000-0000-0000-0000-000000000014');
5
+ insert into agent_board.agents (
6
+ id, agent_type, kind, handle, display_name, status, root_agent_id, auth_user_id
7
+ ) values (
8
+ '61100000-0000-0000-0000-000000000001',
9
+ 'agent', 'agent', 'sid-backfill-root', 'SID Backfill Root', 'active', null,
10
+ '61100000-0000-0000-0000-000000000014'
11
+ );
12
+
13
+ update agent_board.agents set owner_user_id='61100000-0000-0000-0000-000000000010',
14
+ created_at='2026-01-01 00:00:00+00', metadata='{"sid_backfill":"historical"}'
15
+ where id='61100000-0000-0000-0000-000000000001';
16
+ insert into agent_board.agent_signing_keys(id,agent_id,key_id,key_version,public_key,public_key_format,
17
+ fingerprint,status,proof_state,created_at,metadata)
18
+ values ('61100000-0000-0000-0000-000000000011','61100000-0000-0000-0000-000000000001',
19
+ 'historical-backfill',1,public.test_pubkey('sid-historical-backfill'),'raw_ed25519_base64url',
20
+ agent_board._raw_key_fingerprint(public.test_pubkey('sid-historical-backfill')),'pending_proof','unproven',
21
+ '2026-01-01 00:00:00+00','{"sid_backfill":"historical-key"}');
22
+ insert into agent_board.boards(id,slug,name,created_by_agent_id,created_at)
23
+ values ('61100000-0000-0000-0000-000000000012','sid-historical-backfill','Historical backfill',
24
+ '61100000-0000-0000-0000-000000000001','2026-01-01 00:00:00+00');
25
+ insert into agent_board.board_membership_events(id,board_id,agent_id,event_kind,result_state,provenance,created_at)
26
+ values ('61100000-0000-0000-0000-000000000013','61100000-0000-0000-0000-000000000012',
27
+ '61100000-0000-0000-0000-000000000001','historical-placement','joined','operator-designated','2026-01-01 00:00:00+00');
28
+
29
+ insert into agent_board.agents (
30
+ id, agent_type, kind, handle, display_name, status, parent_agent_id, root_agent_id, owner_user_id
31
+ ) values (
32
+ '61100000-0000-0000-0000-000000000002',
33
+ 'agent', 'agent', 'sid-backfill-child', 'SID Backfill Child', 'active',
34
+ '61100000-0000-0000-0000-000000000001',
35
+ '61100000-0000-0000-0000-000000000001',
36
+ '61100000-0000-0000-0000-000000000010'
37
+ );
38
+
39
+ insert into agent_board.agents (
40
+ id, agent_type, kind, handle, display_name, status, parent_agent_id, root_agent_id, owner_user_id
41
+ ) values (
42
+ '61100000-0000-0000-0000-000000000003',
43
+ 'agent', 'agent', 'sid-backfill-grandchild', 'SID Backfill Grandchild', 'active',
44
+ '61100000-0000-0000-0000-000000000002',
45
+ '61100000-0000-0000-0000-000000000001',
46
+ '61100000-0000-0000-0000-000000000010'
47
+ );