@consiliency/agent-board-schema 1.60.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 (68) hide show
  1. package/README.md +80 -0
  2. package/fixtures/already-applied.json +12 -0
  3. package/migrations/20261011000100_agent_board_session_identities.sql +1964 -0
  4. package/migrations/20261012000100_agent_board_sidowner.sql +209 -0
  5. package/migrations/20261014000100_agent_board_grant_target_role.sql +218 -0
  6. package/package.json +1 -1
  7. package/schema-manifest.json +30 -3
  8. package/src/tooling.test.mjs +6 -2
  9. package/tests/agent_board_atomic_fleet_onboarding.test.sql +13 -4
  10. package/tests/agent_board_consent_emission.test.sql +30 -11
  11. package/tests/agent_board_consent_gating.test.sql +16 -2
  12. package/tests/agent_board_delegate_role_class.test.sql +1 -1
  13. package/tests/agent_board_delegation_cap_ceiling.test.sql +26 -15
  14. package/tests/agent_board_delegation_enforcement.test.sql +1 -1
  15. package/tests/agent_board_enroll_resumption.test.sql +22 -4
  16. package/tests/agent_board_enroll_token_single_use.test.sql +18 -3
  17. package/tests/agent_board_federated_lock_order_class.test.sql +2 -2
  18. package/tests/agent_board_fk_lock_edges.test.sql +74 -14
  19. package/tests/agent_board_grant_target_role.test.sql +1118 -0
  20. package/tests/agent_board_invite_no_downgrade.test.sql +10 -8
  21. package/tests/agent_board_ledger_authority.test.sql +70 -12
  22. package/tests/agent_board_membership_current_row_class.test.sql +9 -1
  23. package/tests/agent_board_mint_fleet_agent_session.test.sql +2 -2
  24. package/tests/agent_board_mint_harden.test.sql +38 -27
  25. package/tests/agent_board_rolereset.test.sql +8 -2
  26. package/tests/agent_board_roomdeleg_lane_b.test.sql +6 -6
  27. package/tests/agent_board_rowid.test.sql +3 -1
  28. package/tests/agent_board_session_identity_approval_revocation.test.sql +334 -0
  29. package/tests/agent_board_session_identity_backfill.test.sql +82 -0
  30. package/tests/agent_board_session_identity_backfill_refusal.test.sql +14 -0
  31. package/tests/agent_board_session_identity_behavior_classes.test.sql +176 -0
  32. package/tests/agent_board_session_identity_class.test.sql +118 -0
  33. package/tests/agent_board_session_identity_fingerprint.test.sql +271 -0
  34. package/tests/agent_board_session_identity_labels_limits.test.sql +205 -0
  35. package/tests/agent_board_session_identity_lease.test.sql +235 -0
  36. package/tests/agent_board_session_identity_lifetime.test.sql +230 -0
  37. package/tests/agent_board_session_identity_limits.test.sql +208 -0
  38. package/tests/agent_board_session_identity_lineage.test.sql +64 -0
  39. package/tests/agent_board_session_identity_link.test.sql +201 -0
  40. package/tests/agent_board_session_identity_mint.test.sql +233 -0
  41. package/tests/agent_board_session_identity_panel_r1.test.sql +194 -0
  42. package/tests/agent_board_session_identity_panel_r2.test.sql +174 -0
  43. package/tests/agent_board_session_identity_protected_actions.test.sql +156 -0
  44. package/tests/agent_board_session_identity_revoke_closure.test.sql +131 -0
  45. package/tests/agent_board_sidowner_leases.test.sql +107 -0
  46. package/tests/agent_board_sidowner_owner_required.test.sql +97 -0
  47. package/tests/agent_board_sidowner_read_class.test.sql +171 -0
  48. package/tests/agent_board_sidowner_reads.test.sql +136 -0
  49. package/tests/agent_board_signer_lifecycle.test.sql +26 -4
  50. package/tests/agent_board_signing_challenge_pending_cap.test.sql +1 -1
  51. package/tests/agent_board_write_enforcement_class.test.sql +74 -15
  52. package/tests/catalog/function_execute_privileges.json +218 -2
  53. package/tests/catalog/p_metadata_parameters.json +98 -2
  54. package/tests/isolation/expected/grant_target_role_race.out +348 -0
  55. package/tests/isolation/expected/session_identity_limits.out +165 -0
  56. package/tests/isolation/expected/session_identity_links.out +235 -0
  57. package/tests/isolation/expected/session_identity_reclaim.out +253 -0
  58. package/tests/isolation/expected/session_identity_renew_revoke.out +115 -0
  59. package/tests/isolation/expected/session_identity_renew_revoke_key_gate.out +138 -0
  60. package/tests/isolation/expected/session_identity_tombstone_onboarding.out +247 -0
  61. package/tests/isolation/specs/grant_target_role_race.spec +207 -0
  62. package/tests/isolation/specs/session_identity_limits.spec +81 -0
  63. package/tests/isolation/specs/session_identity_links.spec +80 -0
  64. package/tests/isolation/specs/session_identity_reclaim.spec +125 -0
  65. package/tests/isolation/specs/session_identity_renew_revoke.spec +84 -0
  66. package/tests/isolation/specs/session_identity_renew_revoke_key_gate.spec +94 -0
  67. package/tests/isolation/specs/session_identity_tombstone_onboarding.spec +108 -0
  68. package/tests/seeds/20261011000100_agent_board_session_identities.sql.pre.sql +47 -0
@@ -0,0 +1,235 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: a_begin a_link b_begin b_same a_commit b_commit check
4
+ step a_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
5
+ step a_link: select agent_board.link_session_alias('72000000-0000-4000-8000-000000000002', '0', 'continued-a');
6
+ link_session_alias
7
+ ------------------
8
+
9
+ (1 row)
10
+
11
+ step b_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
12
+ step b_same: select agent_board.link_session_alias('72000000-0000-4000-8000-000000000002', '0', 'continued-a'); <waiting ...>
13
+ step a_commit: COMMIT;
14
+ step b_same: <... completed>
15
+ link_session_alias
16
+ ------------------
17
+
18
+ (1 row)
19
+
20
+ step b_commit: COMMIT;
21
+ step check:
22
+ select count(*) as aliases, count(distinct identity_agent_id) as identities
23
+ from agent_board.session_id_aliases where root_agent_id = '72000000-0000-4000-8000-000000000001';
24
+ select credential_epoch, holder_secret_hash = extensions.digest('isolation-holder', 'sha256') as unchanged_holder
25
+ from agent_board.session_identities where agent_id = '72000000-0000-4000-8000-000000000002';
26
+ select granted_scopes, role from agent_board.delegation_grants g join agent_board.board_memberships bm on bm.agent_id = g.child_agent_id
27
+ where g.child_agent_id = '72000000-0000-4000-8000-000000000002' and bm.board_id = '74000000-0000-4000-8000-000000000002';
28
+
29
+ aliases|identities
30
+ -------+----------
31
+ 2| 1
32
+ (1 row)
33
+
34
+ credential_epoch|unchanged_holder
35
+ ----------------+----------------
36
+ 0|t
37
+ (1 row)
38
+
39
+ granted_scopes |role
40
+ -----------------+-----
41
+ ["read", "write"]|admin
42
+ (1 row)
43
+
44
+
45
+ starting permutation: b_begin b_same a_begin a_link b_commit a_commit check
46
+ step b_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
47
+ step b_same: select agent_board.link_session_alias('72000000-0000-4000-8000-000000000002', '0', 'continued-a');
48
+ link_session_alias
49
+ ------------------
50
+
51
+ (1 row)
52
+
53
+ step a_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
54
+ step a_link: select agent_board.link_session_alias('72000000-0000-4000-8000-000000000002', '0', 'continued-a'); <waiting ...>
55
+ step b_commit: COMMIT;
56
+ step a_link: <... completed>
57
+ link_session_alias
58
+ ------------------
59
+
60
+ (1 row)
61
+
62
+ step a_commit: COMMIT;
63
+ step check:
64
+ select count(*) as aliases, count(distinct identity_agent_id) as identities
65
+ from agent_board.session_id_aliases where root_agent_id = '72000000-0000-4000-8000-000000000001';
66
+ select credential_epoch, holder_secret_hash = extensions.digest('isolation-holder', 'sha256') as unchanged_holder
67
+ from agent_board.session_identities where agent_id = '72000000-0000-4000-8000-000000000002';
68
+ select granted_scopes, role from agent_board.delegation_grants g join agent_board.board_memberships bm on bm.agent_id = g.child_agent_id
69
+ where g.child_agent_id = '72000000-0000-4000-8000-000000000002' and bm.board_id = '74000000-0000-4000-8000-000000000002';
70
+
71
+ aliases|identities
72
+ -------+----------
73
+ 2| 1
74
+ (1 row)
75
+
76
+ credential_epoch|unchanged_holder
77
+ ----------------+----------------
78
+ 0|t
79
+ (1 row)
80
+
81
+ granted_scopes |role
82
+ -----------------+-----
83
+ ["read", "write"]|admin
84
+ (1 row)
85
+
86
+
87
+ starting permutation: a_begin a_link b_begin b_other a_commit b_commit check
88
+ step a_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
89
+ step a_link: select agent_board.link_session_alias('72000000-0000-4000-8000-000000000002', '0', 'continued-a');
90
+ link_session_alias
91
+ ------------------
92
+
93
+ (1 row)
94
+
95
+ step b_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
96
+ step b_other: select agent_board.link_session_alias('72000000-0000-4000-8000-000000000002', '0', 'continued-b'); <waiting ...>
97
+ step a_commit: COMMIT;
98
+ step b_other: <... completed>
99
+ link_session_alias
100
+ ------------------
101
+
102
+ (1 row)
103
+
104
+ step b_commit: COMMIT;
105
+ step check:
106
+ select count(*) as aliases, count(distinct identity_agent_id) as identities
107
+ from agent_board.session_id_aliases where root_agent_id = '72000000-0000-4000-8000-000000000001';
108
+ select credential_epoch, holder_secret_hash = extensions.digest('isolation-holder', 'sha256') as unchanged_holder
109
+ from agent_board.session_identities where agent_id = '72000000-0000-4000-8000-000000000002';
110
+ select granted_scopes, role from agent_board.delegation_grants g join agent_board.board_memberships bm on bm.agent_id = g.child_agent_id
111
+ where g.child_agent_id = '72000000-0000-4000-8000-000000000002' and bm.board_id = '74000000-0000-4000-8000-000000000002';
112
+
113
+ aliases|identities
114
+ -------+----------
115
+ 3| 1
116
+ (1 row)
117
+
118
+ credential_epoch|unchanged_holder
119
+ ----------------+----------------
120
+ 0|t
121
+ (1 row)
122
+
123
+ granted_scopes |role
124
+ -----------------+-----
125
+ ["read", "write"]|admin
126
+ (1 row)
127
+
128
+
129
+ starting permutation: a_begin a_link b_begin b_narrow a_commit b_commit a_rebegin a_relink a_recommit check
130
+ step a_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
131
+ step a_link: select agent_board.link_session_alias('72000000-0000-4000-8000-000000000002', '0', 'continued-a');
132
+ link_session_alias
133
+ ------------------
134
+
135
+ (1 row)
136
+
137
+ step b_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
138
+ step b_narrow:
139
+ select role from agent_board.grant_board_role('74000000-0000-4000-8000-000000000002', '72000000-0000-4000-8000-000000000001', 'observer', '72000000-0000-4000-8000-000000000001');
140
+ select allowed_scopes from agent_board.upsert_delegation_policy('72000000-0000-4000-8000-000000000001', 'root_minted', '["read"]', 200, interval '8 hours', true);
141
+ <waiting ...>
142
+ step a_commit: COMMIT;
143
+ step b_narrow: <... completed>
144
+ role
145
+ --------
146
+ observer
147
+ (1 row)
148
+
149
+ allowed_scopes
150
+ --------------
151
+ ["read"]
152
+ (1 row)
153
+
154
+ step b_commit: COMMIT;
155
+ step a_rebegin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
156
+ step a_relink: select agent_board.link_session_alias('72000000-0000-4000-8000-000000000002', '0', 'continued-a');
157
+ link_session_alias
158
+ ------------------
159
+
160
+ (1 row)
161
+
162
+ step a_recommit: COMMIT;
163
+ step check:
164
+ select count(*) as aliases, count(distinct identity_agent_id) as identities
165
+ from agent_board.session_id_aliases where root_agent_id = '72000000-0000-4000-8000-000000000001';
166
+ select credential_epoch, holder_secret_hash = extensions.digest('isolation-holder', 'sha256') as unchanged_holder
167
+ from agent_board.session_identities where agent_id = '72000000-0000-4000-8000-000000000002';
168
+ select granted_scopes, role from agent_board.delegation_grants g join agent_board.board_memberships bm on bm.agent_id = g.child_agent_id
169
+ where g.child_agent_id = '72000000-0000-4000-8000-000000000002' and bm.board_id = '74000000-0000-4000-8000-000000000002';
170
+
171
+ aliases|identities
172
+ -------+----------
173
+ 2| 1
174
+ (1 row)
175
+
176
+ credential_epoch|unchanged_holder
177
+ ----------------+----------------
178
+ 0|t
179
+ (1 row)
180
+
181
+ granted_scopes|role
182
+ --------------+--------
183
+ ["read"] |observer
184
+ (1 row)
185
+
186
+
187
+ starting permutation: b_begin b_narrow a_begin a_link b_commit a_commit check
188
+ step b_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
189
+ step b_narrow:
190
+ select role from agent_board.grant_board_role('74000000-0000-4000-8000-000000000002', '72000000-0000-4000-8000-000000000001', 'observer', '72000000-0000-4000-8000-000000000001');
191
+ select allowed_scopes from agent_board.upsert_delegation_policy('72000000-0000-4000-8000-000000000001', 'root_minted', '["read"]', 200, interval '8 hours', true);
192
+
193
+ role
194
+ --------
195
+ observer
196
+ (1 row)
197
+
198
+ allowed_scopes
199
+ --------------
200
+ ["read"]
201
+ (1 row)
202
+
203
+ step a_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role'; SET LOCAL "request.jwt.claim.sub" = '71000000-0000-4000-8000-000000000001';
204
+ step a_link: select agent_board.link_session_alias('72000000-0000-4000-8000-000000000002', '0', 'continued-a'); <waiting ...>
205
+ step b_commit: COMMIT;
206
+ step a_link: <... completed>
207
+ link_session_alias
208
+ ------------------
209
+
210
+ (1 row)
211
+
212
+ step a_commit: COMMIT;
213
+ step check:
214
+ select count(*) as aliases, count(distinct identity_agent_id) as identities
215
+ from agent_board.session_id_aliases where root_agent_id = '72000000-0000-4000-8000-000000000001';
216
+ select credential_epoch, holder_secret_hash = extensions.digest('isolation-holder', 'sha256') as unchanged_holder
217
+ from agent_board.session_identities where agent_id = '72000000-0000-4000-8000-000000000002';
218
+ select granted_scopes, role from agent_board.delegation_grants g join agent_board.board_memberships bm on bm.agent_id = g.child_agent_id
219
+ where g.child_agent_id = '72000000-0000-4000-8000-000000000002' and bm.board_id = '74000000-0000-4000-8000-000000000002';
220
+
221
+ aliases|identities
222
+ -------+----------
223
+ 2| 1
224
+ (1 row)
225
+
226
+ credential_epoch|unchanged_holder
227
+ ----------------+----------------
228
+ 0|t
229
+ (1 row)
230
+
231
+ granted_scopes|role
232
+ --------------+--------
233
+ ["read"] |observer
234
+ (1 row)
235
+
@@ -0,0 +1,253 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: a_begin a_reclaim b_begin b_reclaim a_commit b_commit check
4
+ step a_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role';
5
+ step a_reclaim:
6
+ do $$ declare c record; begin
7
+ select * into c from agent_board.issue_signing_key_challenge('75000000-0000-4000-8000-000000000002', 'holder-a');
8
+ perform * from agent_board.mint_fleet_agent_session(c.challenge_id, split_part(c.challenge, ':', 3),
9
+ '76000000-0000-4000-8000-000000000001', '75000000-0000-4000-8000-000000000002', 900, null, 'resumed');
10
+ end; $$;
11
+
12
+ step b_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role';
13
+ step b_reclaim:
14
+ do $$ declare c record; begin
15
+ select * into c from agent_board.issue_signing_key_challenge('75000000-0000-4000-8000-000000000002', 'holder-b');
16
+ perform * from agent_board.mint_fleet_agent_session(c.challenge_id, split_part(c.challenge, ':', 3),
17
+ '76000000-0000-4000-8000-000000000002', '75000000-0000-4000-8000-000000000002', 900, null, 'resumed');
18
+ end; $$;
19
+ <waiting ...>
20
+ step a_commit: COMMIT;
21
+ step b_reclaim: <... completed>
22
+ ERROR: duplicate_live_session
23
+ step b_commit: COMMIT;
24
+ step check:
25
+ select credential_epoch, holder_secret_hash <> extensions.digest('old-holder', 'sha256') as rotated_holder,
26
+ a.session_id as bound_alias
27
+ from agent_board.session_identities si join agent_board.session_id_aliases a on a.id = si.lease_alias_id
28
+ where si.agent_id = '75000000-0000-4000-8000-000000000002';
29
+ select count(*) as tokens from agent_board.nonfleet_sessions where agent_id = '75000000-0000-4000-8000-000000000002';
30
+ BEGIN;
31
+ SET LOCAL ROLE authenticated;
32
+ SET LOCAL "request.jwt.claim.role" = 'authenticated';
33
+ SET LOCAL "request.jwt.claim.sub" = '74000000-0000-4000-8000-000000000001';
34
+ SET LOCAL "request.jwt.claim.identity_lease_epoch" = '0';
35
+ select agent_board.current_agent_id() is null as old_epoch_fenced;
36
+ SET LOCAL "request.jwt.claim.identity_lease_epoch" = '1';
37
+ select agent_board.current_agent_id() = '75000000-0000-4000-8000-000000000002'::uuid as new_epoch_resolves;
38
+ COMMIT;
39
+
40
+ credential_epoch|rotated_holder|bound_alias
41
+ ----------------+--------------+-----------
42
+ 1|t |resumed
43
+ (1 row)
44
+
45
+ tokens
46
+ ------
47
+ 1
48
+ (1 row)
49
+
50
+ old_epoch_fenced
51
+ ----------------
52
+ t
53
+ (1 row)
54
+
55
+ new_epoch_resolves
56
+ ------------------
57
+ t
58
+ (1 row)
59
+
60
+
61
+ starting permutation: b_begin b_reclaim a_begin a_reclaim b_commit a_commit check
62
+ step b_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role';
63
+ step b_reclaim:
64
+ do $$ declare c record; begin
65
+ select * into c from agent_board.issue_signing_key_challenge('75000000-0000-4000-8000-000000000002', 'holder-b');
66
+ perform * from agent_board.mint_fleet_agent_session(c.challenge_id, split_part(c.challenge, ':', 3),
67
+ '76000000-0000-4000-8000-000000000002', '75000000-0000-4000-8000-000000000002', 900, null, 'resumed');
68
+ end; $$;
69
+
70
+ step a_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role';
71
+ step a_reclaim:
72
+ do $$ declare c record; begin
73
+ select * into c from agent_board.issue_signing_key_challenge('75000000-0000-4000-8000-000000000002', 'holder-a');
74
+ perform * from agent_board.mint_fleet_agent_session(c.challenge_id, split_part(c.challenge, ':', 3),
75
+ '76000000-0000-4000-8000-000000000001', '75000000-0000-4000-8000-000000000002', 900, null, 'resumed');
76
+ end; $$;
77
+ <waiting ...>
78
+ step b_commit: COMMIT;
79
+ step a_reclaim: <... completed>
80
+ ERROR: duplicate_live_session
81
+ step a_commit: COMMIT;
82
+ step check:
83
+ select credential_epoch, holder_secret_hash <> extensions.digest('old-holder', 'sha256') as rotated_holder,
84
+ a.session_id as bound_alias
85
+ from agent_board.session_identities si join agent_board.session_id_aliases a on a.id = si.lease_alias_id
86
+ where si.agent_id = '75000000-0000-4000-8000-000000000002';
87
+ select count(*) as tokens from agent_board.nonfleet_sessions where agent_id = '75000000-0000-4000-8000-000000000002';
88
+ BEGIN;
89
+ SET LOCAL ROLE authenticated;
90
+ SET LOCAL "request.jwt.claim.role" = 'authenticated';
91
+ SET LOCAL "request.jwt.claim.sub" = '74000000-0000-4000-8000-000000000001';
92
+ SET LOCAL "request.jwt.claim.identity_lease_epoch" = '0';
93
+ select agent_board.current_agent_id() is null as old_epoch_fenced;
94
+ SET LOCAL "request.jwt.claim.identity_lease_epoch" = '1';
95
+ select agent_board.current_agent_id() = '75000000-0000-4000-8000-000000000002'::uuid as new_epoch_resolves;
96
+ COMMIT;
97
+
98
+ credential_epoch|rotated_holder|bound_alias
99
+ ----------------+--------------+-----------
100
+ 1|t |resumed
101
+ (1 row)
102
+
103
+ tokens
104
+ ------
105
+ 1
106
+ (1 row)
107
+
108
+ old_epoch_fenced
109
+ ----------------
110
+ t
111
+ (1 row)
112
+
113
+ new_epoch_resolves
114
+ ------------------
115
+ t
116
+ (1 row)
117
+
118
+
119
+ starting permutation: prepare_stale a_begin a_first_claim b_begin b_supersede a_commit b_commit check_stale
120
+ step prepare_stale:
121
+ update agent_board.agents set owner_user_id = '74000000-0000-4000-8000-000000000001'
122
+ where id = '75000000-0000-4000-8000-000000000001';
123
+ BEGIN;
124
+ SET LOCAL ROLE service_role;
125
+ SET LOCAL "request.jwt.claim.role" = 'service_role';
126
+ select count(*) from agent_board.upsert_delegation_policy('75000000-0000-4000-8000-000000000001', 'root_minted', '[]', 200, interval '8 hours', true);
127
+ do $$ declare m record; begin
128
+ select * into m from agent_board.mint_delegated_child('75000000-0000-4000-8000-000000000001', 'sid-stale-old', '[]',
129
+ public.test_pubkey('sid-stale-old'), interval '1 hour', false, 'claude', 'stale-reservation', null, '[]',
130
+ jsonb_build_object('resolution', 'resolved', 'risky_digest', 'approved'));
131
+ perform * from agent_board.issue_signing_key_challenge(m.child_agent_id, 'delegated-ephemeral');
132
+ end $$;
133
+ COMMIT;
134
+ update agent_board.session_identities set created_at = now() - interval '181 seconds'
135
+ where agent_id = (select id from agent_board.agents where handle = 'sid-stale-old');
136
+
137
+ count
138
+ -----
139
+ 1
140
+ (1 row)
141
+
142
+ step a_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role';
143
+ step a_first_claim:
144
+ do $$ declare a uuid; k uuid; c record; begin
145
+ select id into a from agent_board.agents where handle = 'sid-stale-old';
146
+ select id into k from agent_board.agent_signing_keys where agent_id = a;
147
+ select id, challenge_nonce into c from agent_board.agent_signing_key_challenges where agent_id = a order by created_at desc limit 1;
148
+ perform * from agent_board.mint_fleet_agent_session(c.id, c.challenge_nonce, k, a, 900, null, 'stale-reservation');
149
+ end $$;
150
+
151
+ step b_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role';
152
+ step b_supersede:
153
+ do $$ begin
154
+ perform * from agent_board.mint_delegated_child('75000000-0000-4000-8000-000000000001', 'sid-stale-new', '[]',
155
+ public.test_pubkey('sid-stale-new'), interval '1 hour', false, 'claude', 'stale-reservation', null, '[]',
156
+ jsonb_build_object('resolution', 'resolved', 'risky_digest', 'approved'));
157
+ end $$;
158
+ <waiting ...>
159
+ step a_commit: COMMIT;
160
+ step b_supersede: <... completed>
161
+ ERROR: duplicate_live_session
162
+ step b_commit: COMMIT;
163
+ step check_stale:
164
+ select a.status, si.tombstone_reason, si.credential_epoch, si.holder_secret_hash is not null as claimed,
165
+ (select count(*) from agent_board.fleet_revocation_events e where e.subject_id = a.id) as permanent_revocations
166
+ from agent_board.agents a join agent_board.session_identities si on si.agent_id = a.id where a.handle = 'sid-stale-old';
167
+ select a.handle as bound_principal from agent_board.session_id_aliases al join agent_board.agents a on a.id = al.identity_agent_id
168
+ where al.root_agent_id = '75000000-0000-4000-8000-000000000001' and al.session_id = 'stale-reservation';
169
+ select count(*) as successor_principals from agent_board.agents where handle = 'sid-stale-new';
170
+
171
+ status|tombstone_reason|credential_epoch|claimed|permanent_revocations
172
+ ------+----------------+----------------+-------+---------------------
173
+ active| | 0|t | 0
174
+ (1 row)
175
+
176
+ bound_principal
177
+ ---------------
178
+ sid-stale-old
179
+ (1 row)
180
+
181
+ successor_principals
182
+ --------------------
183
+ 0
184
+ (1 row)
185
+
186
+
187
+ starting permutation: prepare_stale b_begin b_supersede a_begin a_first_claim b_commit a_commit check_stale
188
+ step prepare_stale:
189
+ update agent_board.agents set owner_user_id = '74000000-0000-4000-8000-000000000001'
190
+ where id = '75000000-0000-4000-8000-000000000001';
191
+ BEGIN;
192
+ SET LOCAL ROLE service_role;
193
+ SET LOCAL "request.jwt.claim.role" = 'service_role';
194
+ select count(*) from agent_board.upsert_delegation_policy('75000000-0000-4000-8000-000000000001', 'root_minted', '[]', 200, interval '8 hours', true);
195
+ do $$ declare m record; begin
196
+ select * into m from agent_board.mint_delegated_child('75000000-0000-4000-8000-000000000001', 'sid-stale-old', '[]',
197
+ public.test_pubkey('sid-stale-old'), interval '1 hour', false, 'claude', 'stale-reservation', null, '[]',
198
+ jsonb_build_object('resolution', 'resolved', 'risky_digest', 'approved'));
199
+ perform * from agent_board.issue_signing_key_challenge(m.child_agent_id, 'delegated-ephemeral');
200
+ end $$;
201
+ COMMIT;
202
+ update agent_board.session_identities set created_at = now() - interval '181 seconds'
203
+ where agent_id = (select id from agent_board.agents where handle = 'sid-stale-old');
204
+
205
+ count
206
+ -----
207
+ 1
208
+ (1 row)
209
+
210
+ step b_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role';
211
+ step b_supersede:
212
+ do $$ begin
213
+ perform * from agent_board.mint_delegated_child('75000000-0000-4000-8000-000000000001', 'sid-stale-new', '[]',
214
+ public.test_pubkey('sid-stale-new'), interval '1 hour', false, 'claude', 'stale-reservation', null, '[]',
215
+ jsonb_build_object('resolution', 'resolved', 'risky_digest', 'approved'));
216
+ end $$;
217
+
218
+ step a_begin: BEGIN; SET LOCAL ROLE service_role; SET LOCAL "request.jwt.claim.role" = 'service_role';
219
+ step a_first_claim:
220
+ do $$ declare a uuid; k uuid; c record; begin
221
+ select id into a from agent_board.agents where handle = 'sid-stale-old';
222
+ select id into k from agent_board.agent_signing_keys where agent_id = a;
223
+ select id, challenge_nonce into c from agent_board.agent_signing_key_challenges where agent_id = a order by created_at desc limit 1;
224
+ perform * from agent_board.mint_fleet_agent_session(c.id, c.challenge_nonce, k, a, 900, null, 'stale-reservation');
225
+ end $$;
226
+ <waiting ...>
227
+ step b_commit: COMMIT;
228
+ step a_first_claim: <... completed>
229
+ ERROR: identity_tombstoned
230
+ step a_commit: COMMIT;
231
+ step check_stale:
232
+ select a.status, si.tombstone_reason, si.credential_epoch, si.holder_secret_hash is not null as claimed,
233
+ (select count(*) from agent_board.fleet_revocation_events e where e.subject_id = a.id) as permanent_revocations
234
+ from agent_board.agents a join agent_board.session_identities si on si.agent_id = a.id where a.handle = 'sid-stale-old';
235
+ select a.handle as bound_principal from agent_board.session_id_aliases al join agent_board.agents a on a.id = al.identity_agent_id
236
+ where al.root_agent_id = '75000000-0000-4000-8000-000000000001' and al.session_id = 'stale-reservation';
237
+ select count(*) as successor_principals from agent_board.agents where handle = 'sid-stale-new';
238
+
239
+ status |tombstone_reason |credential_epoch|claimed|permanent_revocations
240
+ -------+--------------------+----------------+-------+---------------------
241
+ revoked|superseded_unclaimed| 1|f | 1
242
+ (1 row)
243
+
244
+ bound_principal
245
+ ---------------
246
+ sid-stale-new
247
+ (1 row)
248
+
249
+ successor_principals
250
+ --------------------
251
+ 1
252
+ (1 row)
253
+
@@ -0,0 +1,115 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: renew_begin renew revoke_begin revoke renew_commit revoke_commit check
4
+ step renew_begin:
5
+ BEGIN;
6
+ 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;
7
+ SET LOCAL ROLE service_role;
8
+ SET LOCAL "request.jwt.claim.role" = 'service_role';
9
+ SET LOCAL "request.jwt.claim.identity_lease_epoch" = '0';
10
+
11
+ holder_forwarded
12
+ ----------------
13
+ t
14
+ (1 row)
15
+
16
+ step renew:
17
+ select agent_board.renew_session_identity((select id from agent_board.agents where handle = 'sid-renew-child'), 'approved');
18
+
19
+ renew_session_identity
20
+ ----------------------
21
+
22
+ (1 row)
23
+
24
+ step revoke_begin:
25
+ BEGIN;
26
+ SET LOCAL ROLE authenticated;
27
+ SET LOCAL "request.jwt.claim.role" = 'authenticated';
28
+ SET LOCAL "request.jwt.claim.sub" = '81000000-0000-4000-8000-000000000001';
29
+
30
+ step revoke:
31
+ select agent_board.revoke_session_identity((select id from agent_board.agents where handle = 'sid-renew-child'));
32
+ <waiting ...>
33
+ step renew_commit: COMMIT;
34
+ step revoke: <... completed>
35
+ revoke_session_identity
36
+ -----------------------
37
+
38
+ (1 row)
39
+
40
+ step revoke_commit: COMMIT;
41
+ step check:
42
+ select si.tombstone_reason, si.credential_epoch, a.status, g.revoked_at is not null as grant_revoked,
43
+ g.last_renewed_at is not null as renewal_preceded_revoke,
44
+ (select count(*) from agent_board.fleet_revocation_events e where e.subject_id = a.id) as permanent_revocations,
45
+ (select count(*) from agent_board.agent_signing_keys k where k.agent_id = a.id and k.status <> 'revoked') as active_keys
46
+ from agent_board.agents a join agent_board.session_identities si on si.agent_id = a.id
47
+ join agent_board.delegation_grants g on g.child_agent_id = a.id where a.handle = 'sid-renew-child';
48
+ select bool_and(expires_at <= issued_at + interval '900 seconds') as bounded_pre_revoke_tokens
49
+ from agent_board.nonfleet_sessions where agent_id = (select id from agent_board.agents where handle = 'sid-renew-child');
50
+
51
+ tombstone_reason|credential_epoch|status |grant_revoked|renewal_preceded_revoke|permanent_revocations|active_keys
52
+ ----------------+----------------+-------+-------------+-----------------------+---------------------+-----------
53
+ revoked | 1|revoked|t |t | 1| 0
54
+ (1 row)
55
+
56
+ bounded_pre_revoke_tokens
57
+ -------------------------
58
+ t
59
+ (1 row)
60
+
61
+
62
+ starting permutation: revoke_begin revoke renew_begin renew revoke_commit renew_commit check
63
+ step revoke_begin:
64
+ BEGIN;
65
+ SET LOCAL ROLE authenticated;
66
+ SET LOCAL "request.jwt.claim.role" = 'authenticated';
67
+ SET LOCAL "request.jwt.claim.sub" = '81000000-0000-4000-8000-000000000001';
68
+
69
+ step revoke:
70
+ select agent_board.revoke_session_identity((select id from agent_board.agents where handle = 'sid-renew-child'));
71
+
72
+ revoke_session_identity
73
+ -----------------------
74
+
75
+ (1 row)
76
+
77
+ step renew_begin:
78
+ BEGIN;
79
+ 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;
80
+ SET LOCAL ROLE service_role;
81
+ SET LOCAL "request.jwt.claim.role" = 'service_role';
82
+ SET LOCAL "request.jwt.claim.identity_lease_epoch" = '0';
83
+
84
+ holder_forwarded
85
+ ----------------
86
+ t
87
+ (1 row)
88
+
89
+ step renew:
90
+ select agent_board.renew_session_identity((select id from agent_board.agents where handle = 'sid-renew-child'), 'approved');
91
+ <waiting ...>
92
+ step revoke_commit: COMMIT;
93
+ step renew: <... completed>
94
+ ERROR: identity_tombstoned
95
+ step renew_commit: COMMIT;
96
+ step check:
97
+ select si.tombstone_reason, si.credential_epoch, a.status, g.revoked_at is not null as grant_revoked,
98
+ g.last_renewed_at is not null as renewal_preceded_revoke,
99
+ (select count(*) from agent_board.fleet_revocation_events e where e.subject_id = a.id) as permanent_revocations,
100
+ (select count(*) from agent_board.agent_signing_keys k where k.agent_id = a.id and k.status <> 'revoked') as active_keys
101
+ from agent_board.agents a join agent_board.session_identities si on si.agent_id = a.id
102
+ join agent_board.delegation_grants g on g.child_agent_id = a.id where a.handle = 'sid-renew-child';
103
+ select bool_and(expires_at <= issued_at + interval '900 seconds') as bounded_pre_revoke_tokens
104
+ from agent_board.nonfleet_sessions where agent_id = (select id from agent_board.agents where handle = 'sid-renew-child');
105
+
106
+ tombstone_reason|credential_epoch|status |grant_revoked|renewal_preceded_revoke|permanent_revocations|active_keys
107
+ ----------------+----------------+-------+-------------+-----------------------+---------------------+-----------
108
+ revoked | 1|revoked|t |f | 1| 0
109
+ (1 row)
110
+
111
+ bounded_pre_revoke_tokens
112
+ -------------------------
113
+ t
114
+ (1 row)
115
+