@consiliency/agent-board-schema 1.60.0 → 1.64.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/README.md +86 -0
- package/fixtures/already-applied.json +16 -0
- package/migrations/20261011000100_agent_board_session_identities.sql +1964 -0
- package/migrations/20261012000100_agent_board_sidowner.sql +209 -0
- package/migrations/20261014000100_agent_board_grant_target_role.sql +218 -0
- package/migrations/20261014000200_agent_board_last_owner.sql +239 -0
- package/package.json +1 -1
- package/schema-manifest.json +39 -3
- package/src/tooling.test.mjs +10 -2
- package/tests/agent_board_atomic_fleet_onboarding.test.sql +15 -4
- package/tests/agent_board_atomic_fleet_onboarding_runtime.test.sql +7 -0
- package/tests/agent_board_consent_emission.test.sql +30 -11
- package/tests/agent_board_consent_gating.test.sql +16 -2
- package/tests/agent_board_delegate_role_class.test.sql +1 -1
- package/tests/agent_board_delegation_cap_ceiling.test.sql +26 -15
- package/tests/agent_board_delegation_enforcement.test.sql +1 -1
- package/tests/agent_board_enroll_resumption.test.sql +22 -4
- package/tests/agent_board_enroll_token_single_use.test.sql +18 -3
- package/tests/agent_board_federated_lock_order_class.test.sql +2 -2
- package/tests/agent_board_fk_lock_edges.test.sql +77 -16
- package/tests/agent_board_grant_target_role.test.sql +1118 -0
- package/tests/agent_board_hosted_delegates.test.sql +5 -3
- package/tests/agent_board_invite_no_downgrade.test.sql +15 -11
- package/tests/agent_board_ledger_authority.test.sql +77 -12
- package/tests/agent_board_membership_current_row_class.test.sql +9 -1
- package/tests/agent_board_mint_fleet_agent_session.test.sql +2 -2
- package/tests/agent_board_mint_harden.test.sql +38 -27
- package/tests/agent_board_role_guard.test.sql +319 -0
- package/tests/agent_board_rolereset.test.sql +25 -29
- package/tests/agent_board_roomdeleg.test.sql +8 -0
- package/tests/agent_board_roomdeleg_lane_b.test.sql +6 -6
- package/tests/agent_board_roomown.test.sql +3 -1
- package/tests/agent_board_rowid.test.sql +7 -2
- package/tests/agent_board_session_identity_approval_revocation.test.sql +334 -0
- package/tests/agent_board_session_identity_backfill.test.sql +82 -0
- package/tests/agent_board_session_identity_backfill_refusal.test.sql +14 -0
- package/tests/agent_board_session_identity_behavior_classes.test.sql +176 -0
- package/tests/agent_board_session_identity_class.test.sql +118 -0
- package/tests/agent_board_session_identity_fingerprint.test.sql +271 -0
- package/tests/agent_board_session_identity_labels_limits.test.sql +205 -0
- package/tests/agent_board_session_identity_lease.test.sql +235 -0
- package/tests/agent_board_session_identity_lifetime.test.sql +230 -0
- package/tests/agent_board_session_identity_limits.test.sql +208 -0
- package/tests/agent_board_session_identity_lineage.test.sql +64 -0
- package/tests/agent_board_session_identity_link.test.sql +201 -0
- package/tests/agent_board_session_identity_mint.test.sql +233 -0
- package/tests/agent_board_session_identity_panel_r1.test.sql +194 -0
- package/tests/agent_board_session_identity_panel_r2.test.sql +174 -0
- package/tests/agent_board_session_identity_protected_actions.test.sql +156 -0
- package/tests/agent_board_session_identity_revoke_closure.test.sql +131 -0
- package/tests/agent_board_sidowner_leases.test.sql +107 -0
- package/tests/agent_board_sidowner_owner_required.test.sql +97 -0
- package/tests/agent_board_sidowner_read_class.test.sql +171 -0
- package/tests/agent_board_sidowner_reads.test.sql +136 -0
- package/tests/agent_board_signer_lifecycle.test.sql +26 -4
- package/tests/agent_board_signing_challenge_pending_cap.test.sql +1 -1
- package/tests/agent_board_write_enforcement_class.test.sql +74 -15
- package/tests/catalog/function_execute_privileges.json +227 -2
- package/tests/catalog/p_metadata_parameters.json +102 -2
- package/tests/isolation/expected/grant_target_role_race.out +348 -0
- package/tests/isolation/expected/last_owner_departure.out +562 -0
- package/tests/isolation/expected/session_identity_limits.out +165 -0
- package/tests/isolation/expected/session_identity_links.out +235 -0
- package/tests/isolation/expected/session_identity_reclaim.out +253 -0
- package/tests/isolation/expected/session_identity_renew_revoke.out +115 -0
- package/tests/isolation/expected/session_identity_renew_revoke_key_gate.out +138 -0
- package/tests/isolation/expected/session_identity_tombstone_onboarding.out +247 -0
- package/tests/isolation/specs/atomic_fleet_authority_races.spec +8 -3
- package/tests/isolation/specs/atomic_fleet_binding_revoke.spec +0 -3
- package/tests/isolation/specs/atomic_fleet_board_archive.spec +0 -3
- package/tests/isolation/specs/atomic_fleet_human_fresh_revoke.spec +0 -3
- package/tests/isolation/specs/atomic_fleet_identity_disposition.spec +0 -3
- package/tests/isolation/specs/atomic_fleet_lifecycle_races.spec +0 -3
- package/tests/isolation/specs/atomic_fleet_subtree_lock_order.spec +0 -3
- package/tests/isolation/specs/ceiling_setter_lock_before_upsert.spec +0 -3
- package/tests/isolation/specs/claimpolicy_acquire_revert_race.spec +0 -2
- package/tests/isolation/specs/claimpolicy_board_scope.spec +0 -2
- package/tests/isolation/specs/claimpolicy_claimrow_lock_order.spec +0 -2
- package/tests/isolation/specs/claimpolicy_isolation_refusal.spec +0 -2
- package/tests/isolation/specs/claimpolicy_renew_revert_race.spec +0 -2
- package/tests/isolation/specs/claimpolicy_writer_keyshare.spec +0 -2
- package/tests/isolation/specs/consent_approval_lock.spec +0 -1
- package/tests/isolation/specs/consent_emit_actual_state.spec +0 -1
- package/tests/isolation/specs/grant_target_role_race.spec +206 -0
- package/tests/isolation/specs/hosted_delegate_add_tombstone_race.spec +0 -1
- package/tests/isolation/specs/hosted_delegate_tombstone_revoke_race.spec +0 -1
- package/tests/isolation/specs/hosted_delegate_withdraw_evict_race.spec +0 -1
- package/tests/isolation/specs/last_owner_departure.spec +137 -0
- package/tests/isolation/specs/ledger_authority_revoke.spec +0 -3
- package/tests/isolation/specs/onboarding_ceiling_lock_order.spec +0 -3
- package/tests/isolation/specs/outbox_wake_ordering.spec +0 -1
- package/tests/isolation/specs/session_identity_limits.spec +81 -0
- package/tests/isolation/specs/session_identity_links.spec +82 -0
- package/tests/isolation/specs/session_identity_reclaim.spec +125 -0
- package/tests/isolation/specs/session_identity_renew_revoke.spec +84 -0
- package/tests/isolation/specs/session_identity_renew_revoke_key_gate.spec +94 -0
- package/tests/isolation/specs/session_identity_tombstone_onboarding.spec +107 -0
- package/tests/isolation/specs/signed_grant_onboarding_deadlock.spec +0 -1
- package/tests/isolation/specs/signer_lifecycle_revoke.spec +0 -1
- package/tests/seeds/20261011000100_agent_board_session_identities.sql.pre.sql +47 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@consiliency/agent-board-schema",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.64.0",
|
|
4
4
|
"description": "Canonical, immutable, exact-pinnable agent_board schema bundle: ordered SQL migrations, digest + compatibility manifest, and deterministic materialize/plan/check tooling for adopters.",
|
|
5
5
|
"repository": "https://github.com/Consiliency/message-board.git",
|
|
6
6
|
"type": "module",
|
package/schema-manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema": "agent_board_schema_manifest.v1",
|
|
3
3
|
"interface": "IF-0-MBPLANE-BUNDLE-1",
|
|
4
4
|
"package": "@consiliency/agent-board-schema",
|
|
5
|
-
"packageVersion": "1.
|
|
5
|
+
"packageVersion": "1.64.0",
|
|
6
6
|
"schemaVersion": 8,
|
|
7
7
|
"minClientVersion": "1.1.0",
|
|
8
8
|
"source": {
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"usage": "upsert (retention/monitoring defaults; on conflict do nothing)"
|
|
47
47
|
}
|
|
48
48
|
],
|
|
49
|
-
"migrationCount":
|
|
50
|
-
"aggregateSha256": "
|
|
49
|
+
"migrationCount": 162,
|
|
50
|
+
"aggregateSha256": "b6b24a1c50a1ac51cd935f6ad2091876a0b0ff98f001d776c89445a889416745",
|
|
51
51
|
"migrations": [
|
|
52
52
|
{
|
|
53
53
|
"order": 0,
|
|
@@ -1470,6 +1470,42 @@
|
|
|
1470
1470
|
"sourceRef": "message-board",
|
|
1471
1471
|
"bytes": 25831,
|
|
1472
1472
|
"sha256": "6cbbe0bb9ccf7ab65402a58de145ee21bc2158ddc7502671ea13a98415ca6421"
|
|
1473
|
+
},
|
|
1474
|
+
{
|
|
1475
|
+
"order": 158,
|
|
1476
|
+
"id": "20261011000100_agent_board_session_identities",
|
|
1477
|
+
"filename": "20261011000100_agent_board_session_identities.sql",
|
|
1478
|
+
"origin": "message-board-forward",
|
|
1479
|
+
"sourceRef": "message-board",
|
|
1480
|
+
"bytes": 119197,
|
|
1481
|
+
"sha256": "0f429d2b7c2a361741fb00019a173c82024a59362ea2d3a70cb11ab594b1d055"
|
|
1482
|
+
},
|
|
1483
|
+
{
|
|
1484
|
+
"order": 159,
|
|
1485
|
+
"id": "20261012000100_agent_board_sidowner",
|
|
1486
|
+
"filename": "20261012000100_agent_board_sidowner.sql",
|
|
1487
|
+
"origin": "message-board-forward",
|
|
1488
|
+
"sourceRef": "message-board",
|
|
1489
|
+
"bytes": 9979,
|
|
1490
|
+
"sha256": "e18fb5a7ce0b00e9cccf12a60cbad49d820612c2c50f65c648d4105c80fbc921"
|
|
1491
|
+
},
|
|
1492
|
+
{
|
|
1493
|
+
"order": 160,
|
|
1494
|
+
"id": "20261014000100_agent_board_grant_target_role",
|
|
1495
|
+
"filename": "20261014000100_agent_board_grant_target_role.sql",
|
|
1496
|
+
"origin": "message-board-forward",
|
|
1497
|
+
"sourceRef": "message-board",
|
|
1498
|
+
"bytes": 14704,
|
|
1499
|
+
"sha256": "4a6c00546a1d2837f88eece1de082cc76eec1f85cce8cf9ac7998e42bf32a5c5"
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
"order": 161,
|
|
1503
|
+
"id": "20261014000200_agent_board_last_owner",
|
|
1504
|
+
"filename": "20261014000200_agent_board_last_owner.sql",
|
|
1505
|
+
"origin": "message-board-forward",
|
|
1506
|
+
"sourceRef": "message-board",
|
|
1507
|
+
"bytes": 13141,
|
|
1508
|
+
"sha256": "2fed8fc5a2c7c0ba9acbb12872b329db3f0902d96597c52cc50e21e4682c2fff"
|
|
1473
1509
|
}
|
|
1474
1510
|
],
|
|
1475
1511
|
"excludedBridge": {
|
package/src/tooling.test.mjs
CHANGED
|
@@ -24,6 +24,9 @@ const historyExact = () => ({ applied: ordered.map((m) => ({ id: m.id, sha256: m
|
|
|
24
24
|
const union = () => ordered.filter((m) => m.origin !== 'message-board-forward');
|
|
25
25
|
|
|
26
26
|
describe('manifest integrity', () => {
|
|
27
|
+
it('pins the exact owner-authorized A3/A10.3 test amendment for Consiliency/message-board#675', () => {
|
|
28
|
+
expect(sha256(readFileSync(new URL('../tests/agent_board_grant_target_role.test.sql', import.meta.url)))).toBe('5c0a857d8c138f368235fa345a18c76fafa773cfe8eb57185b232d466d222f93');
|
|
29
|
+
});
|
|
27
30
|
it('aggregate digest matches recomputation over ordered per-file digests', () => {
|
|
28
31
|
const agg = sha256(Buffer.from(ordered.map((e) => `${e.order} ${e.id} ${e.sha256}`).join('\n')));
|
|
29
32
|
expect(agg).toBe(manifest.aggregateSha256);
|
|
@@ -38,9 +41,9 @@ describe('manifest integrity', () => {
|
|
|
38
41
|
expect(manifest.schemaVersion).toBe(8);
|
|
39
42
|
expect(manifest.minClientVersion).toMatch(/^\d+\.\d+\.\d+$/);
|
|
40
43
|
});
|
|
41
|
-
it('registers the four MBAUTHZ + four MBSENDAUTHZ + four MBSESSION + four MBDELEG + three MBFED + three MBHARDEN + two MBPORTALGATE + two MBPORTAL-PR32 + two ROOMOWN + two ROOMLINK + one ROOMDELEG + one LOCALAUTH + one ROOMUIAPI + one FEDSESSION + one FEDSESSION-AUTOJOIN + one GATENULL + one MEMBSTATE + one AGENTSDIR + one LEAVEIDEM + one ROLERESET + one MBROWID + one MBHYGIENE + one MBCONSENT-A + one MBCONSENT-B(a) + one MBCONSENT-B(i) + one MBCONSENT-B(ii) + one MBCONSENT-B(iii) + one MBHARDEN-#97 + one MBHARDEN-#118 + one INVITEPOLICY + one OBSGATE + one OBSGATE-2 + one OBSGATE-3 + one OBSGATE-4 + one FLEETMACHINERLS + one RESOLVENOOP + one ROOMDELEG-LANEB + one CLAIMKEY + two MINTHARDEN + one FLEETMACHINEHIJACK + one CLAIMGATE-SHADOW + one CLAIMGATE-ENFORCE + one FLEETMACHINEREACTIVATION + one DELEGCAPCEILING + one FLEETMACHINERECLAIM + one DELEGSOLEWRITER + one FLEETDOMAINRECLAIM + five ATOMICFLEETONBOARDING (onboarding + ceiling-lock + revocation-evidence + fedsession-gate + fedsession-lock-order) + one OUTBOXWAKE + one ENROLLLOCKORDER + one FEDPROVIDERABSENCE + one WAKERECOVERY + one CONSENTRECONCILE + one ENROLLRESUMPTION + one SIGNEDAUTHORITY + one SIGNERLIFECYCLE + one LEDGERAUTHORITY + one CEILINGLOCKCOMMENT + one LEDGERBOARDCREATE + one RECEIPTSUBJECTGATE + one TASKTHREADLOCKORDER + one CLAIMSUBJECTGATE + one FEDSESSIONIDENTITYGATE + one FEDIDENTITYSLUG + one INVITENODOWNGRADE + one SIGNEDWRITEFLAG + one SIGNEDWRITEMODEL + one RTCREDCLAIM + one ORIGINPOSTURE + one BOARDCLAIMPOLICY + one HEARTBEATPRESERVE + one LIVENESSDENOM + one OWNERLESSRENEWTTL + one OWNERLESSQUOTACENSUS + one FLEETDOMAINNODOWNGRADE + one OWNERLESSQUOTACONVERGE + one ADVISORYREVERTRESET + one CLAIMPOLICYRACE + one CLAIMPOLICYLOCKGRANT + one SESSIONCHALLENGESUPERSEDE + one ROOMCONTRACT + one CALLBACKCONSUMER + one ENROLLTOKENSINGLEUSE + one ROOMCONTRACT-ACTIVEFIRST + one ONBOARD + one HOSTED + one HOSTED-FOLLOWUPS + one OWNPOST forward migrations (migrationCount = prior 113 + 5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 =
|
|
44
|
+
it('registers the four MBAUTHZ + four MBSENDAUTHZ + four MBSESSION + four MBDELEG + three MBFED + three MBHARDEN + two MBPORTALGATE + two MBPORTAL-PR32 + two ROOMOWN + two ROOMLINK + one ROOMDELEG + one LOCALAUTH + one ROOMUIAPI + one FEDSESSION + one FEDSESSION-AUTOJOIN + one GATENULL + one MEMBSTATE + one AGENTSDIR + one LEAVEIDEM + one ROLERESET + one MBROWID + one MBHYGIENE + one MBCONSENT-A + one MBCONSENT-B(a) + one MBCONSENT-B(i) + one MBCONSENT-B(ii) + one MBCONSENT-B(iii) + one MBHARDEN-#97 + one MBHARDEN-#118 + one INVITEPOLICY + one OBSGATE + one OBSGATE-2 + one OBSGATE-3 + one OBSGATE-4 + one FLEETMACHINERLS + one RESOLVENOOP + one ROOMDELEG-LANEB + one CLAIMKEY + two MINTHARDEN + one FLEETMACHINEHIJACK + one CLAIMGATE-SHADOW + one CLAIMGATE-ENFORCE + one FLEETMACHINEREACTIVATION + one DELEGCAPCEILING + one FLEETMACHINERECLAIM + one DELEGSOLEWRITER + one FLEETDOMAINRECLAIM + five ATOMICFLEETONBOARDING (onboarding + ceiling-lock + revocation-evidence + fedsession-gate + fedsession-lock-order) + one OUTBOXWAKE + one ENROLLLOCKORDER + one FEDPROVIDERABSENCE + one WAKERECOVERY + one CONSENTRECONCILE + one ENROLLRESUMPTION + one SIGNEDAUTHORITY + one SIGNERLIFECYCLE + one LEDGERAUTHORITY + one CEILINGLOCKCOMMENT + one LEDGERBOARDCREATE + one RECEIPTSUBJECTGATE + one TASKTHREADLOCKORDER + one CLAIMSUBJECTGATE + one FEDSESSIONIDENTITYGATE + one FEDIDENTITYSLUG + one INVITENODOWNGRADE + one SIGNEDWRITEFLAG + one SIGNEDWRITEMODEL + one RTCREDCLAIM + one ORIGINPOSTURE + one BOARDCLAIMPOLICY + one HEARTBEATPRESERVE + one LIVENESSDENOM + one OWNERLESSRENEWTTL + one OWNERLESSQUOTACENSUS + one FLEETDOMAINNODOWNGRADE + one OWNERLESSQUOTACONVERGE + one ADVISORYREVERTRESET + one CLAIMPOLICYRACE + one CLAIMPOLICYLOCKGRANT + one SESSIONCHALLENGESUPERSEDE + one ROOMCONTRACT + one CALLBACKCONSUMER + one ENROLLTOKENSINGLEUSE + one ROOMCONTRACT-ACTIVEFIRST + one ONBOARD + one HOSTED + one HOSTED-FOLLOWUPS + one OWNPOST + one SIDSCHEMA + one SIDOWNER + one GRANTTARGETROLE (#674) + one LASTOWNER (Consiliency/message-board#675) forward migrations (migrationCount = prior 113 + 5 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 162)', () => {
|
|
42
45
|
expect(manifest.migrationCount).toBe(manifest.migrations.length);
|
|
43
|
-
expect(manifest.migrationCount).toBe(
|
|
46
|
+
expect(manifest.migrationCount).toBe(162);
|
|
44
47
|
// MBSESSION switched enforcement on (scoped-send re-grant): the client-visible
|
|
45
48
|
// schema version bumped to 8. MBDELEG tightens delegation and MBFED adds only
|
|
46
49
|
// enrollment-only additive surface (identity_providers registry, enrollment audit,
|
|
@@ -356,6 +359,11 @@ describe('manifest integrity', () => {
|
|
|
356
359
|
// OWNPOST (#608): an agent's own board post is a feed-only row for it, not an inbox item;
|
|
357
360
|
// a board-wide task fan-out skips its creator; own_inbox callbacks skip feed-only rows.
|
|
358
361
|
'20261010000100_agent_board_own_post_not_delivered.sql',
|
|
362
|
+
'20261011000100_agent_board_session_identities.sql',
|
|
363
|
+
'20261012000100_agent_board_sidowner.sql',
|
|
364
|
+
// #674: grant_board_role checks the target's current role (C-A + C-B).
|
|
365
|
+
'20261014000100_agent_board_grant_target_role.sql',
|
|
366
|
+
'20261014000200_agent_board_last_owner.sql',
|
|
359
367
|
]) {
|
|
360
368
|
expect(names.has(f)).toBe(true);
|
|
361
369
|
}
|
|
@@ -307,11 +307,11 @@ select ok(
|
|
|
307
307
|
'agent fleet revocation takes its machine locks before the agent lock [SOLE PIN: no isolation spec discriminates this ordering -- measured against all 20]'
|
|
308
308
|
);
|
|
309
309
|
select ok(
|
|
310
|
-
strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agents a where a.id =
|
|
310
|
+
strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agents a where a.id = v_id for update'
|
|
311
311
|
) > 0
|
|
312
312
|
and strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agent_signing_keys'
|
|
313
313
|
) > 0
|
|
314
|
-
and strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agents a where a.id =
|
|
314
|
+
and strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agents a where a.id = v_id for update'
|
|
315
315
|
) < strpos(lower(agent_board_test.stripped_body('agent_board._revoke_agent_principal(uuid,uuid,text)'::regprocedure)), 'from agent_board.agent_signing_keys'
|
|
316
316
|
),
|
|
317
317
|
'subtree primitive locks the agent before signing keys [SOLE PIN: no isolation spec discriminates this ordering -- measured against all 20]'
|
|
@@ -558,7 +558,10 @@ select is( -- pgtap-allow-raise-checked: catalog read only; the RPC names below
|
|
|
558
558
|
-- lock also closes this reader's exposure to the ceiling row's ABSENCE, one of the three
|
|
559
559
|
-- 20260901000110 left explicitly undetermined.
|
|
560
560
|
'agent_board.grant_board_role(uuid,uuid,text,uuid) => serialized',
|
|
561
|
-
'agent_board.
|
|
561
|
+
'agent_board.link_session_alias(uuid,text,text) => serialized',
|
|
562
|
+
'agent_board.mint_delegated_child(uuid,text,jsonb,text,interval,boolean,text,text,text,jsonb,jsonb,uuid) => serialized',
|
|
563
|
+
'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text) => serialized',
|
|
564
|
+
'agent_board.readmit_session_identity(uuid,text,text,jsonb,jsonb,interval) => serialized'
|
|
562
565
|
]::text[],
|
|
563
566
|
'every ceiling-authorization reader in pg_proc is declared with its serialization state'
|
|
564
567
|
);
|
|
@@ -697,6 +700,7 @@ select is( -- pgtap-allow-raise-checked: catalog read only; the RPC names below
|
|
|
697
700
|
-- than naming the table itself. Internal — it reads rows for an arbitrary agent id with no
|
|
698
701
|
-- authorization of its own, so service_role only, like every other helper here.
|
|
699
702
|
'agent_board._actor_fleet_revoked(uuid) [definer] => service_role',
|
|
703
|
+
'agent_board._board_memberships_last_owner_guard() [definer] => (none)',
|
|
700
704
|
'agent_board._lock_board_authority_ceiling(uuid) [invoker] => service_role',
|
|
701
705
|
'agent_board._redeem_fleet_invite_for_agent(text,uuid) [definer] => service_role',
|
|
702
706
|
'agent_board._redeem_invite_for_agent(text,uuid) [definer] => service_role',
|
|
@@ -718,8 +722,11 @@ select is( -- pgtap-allow-raise-checked: catalog read only; the RPC names below
|
|
|
718
722
|
-- once the ledger gate made it lock both). Still authenticated,service_role — #276 removes
|
|
719
723
|
-- authority from a revoked principal, it does not change who may call the RPC.
|
|
720
724
|
'agent_board.grant_board_role(uuid,uuid,text,uuid) [definer] => authenticated,service_role',
|
|
725
|
+
'agent_board.link_session_alias(uuid,text,text) [definer] => service_role',
|
|
726
|
+
'agent_board.mint_delegated_child(uuid,text,jsonb,text,interval,boolean,text,text,text,jsonb,jsonb,uuid) [definer] => service_role',
|
|
721
727
|
'agent_board.onboard_federated_fleet_agent(text,text,text,jsonb,text,text,text,text,boolean,text,timestamp with time zone,text,text,text[],text[],text,uuid,uuid,text,text,text,text,text,text,text) [definer] => service_role',
|
|
722
728
|
'agent_board.reactivate_fleet_machine(uuid,uuid,text) [definer] => service_role',
|
|
729
|
+
'agent_board.readmit_session_identity(uuid,text,text,jsonb,jsonb,interval) [definer] => authenticated',
|
|
723
730
|
'agent_board.revoke_fleet_subject(text,uuid,uuid,text,jsonb) [definer] => authenticated,service_role',
|
|
724
731
|
'agent_board.revoke_human_subtree(uuid,uuid,text) [definer] => service_role',
|
|
725
732
|
'agent_board.set_board_authority_ceiling(uuid,text,uuid) [definer] => authenticated,service_role',
|
|
@@ -727,7 +734,11 @@ select is( -- pgtap-allow-raise-checked: catalog read only; the RPC names below
|
|
|
727
734
|
-- FIRST, because its order was board_memberships(grantee) -> agents(signer) against
|
|
728
735
|
-- onboard_federated_fleet_agent's agents -> board_memberships -- a reproduced ABBA between
|
|
729
736
|
-- two honest callers. It is service_role-only like every other signed_* wrapper (#118).
|
|
730
|
-
'agent_board.signed_grant_board_role(jsonb,jsonb) [definer] => service_role'
|
|
737
|
+
'agent_board.signed_grant_board_role(jsonb,jsonb) [definer] => service_role',
|
|
738
|
+
-- SIDSCHEMA integrity repair: the verified Portal withdrawal now takes board mutexes
|
|
739
|
+
-- before the instance/family rows. Its service-only transport is unchanged.
|
|
740
|
+
'agent_board.tombstone_delegate_instance(text,text,text,text) [definer] => service_role',
|
|
741
|
+
'agent_board.update_board_membership(uuid,uuid,text,jsonb) [definer] => authenticated,service_role'
|
|
731
742
|
]::text[],
|
|
732
743
|
'every function on this migration set''s surface is declared with its definer flag and exact EXECUTE grantees'
|
|
733
744
|
);
|
|
@@ -837,6 +837,13 @@ select is(
|
|
|
837
837
|
'0/0', 'generic-invite rejection leaves uses and redemption counts unchanged'
|
|
838
838
|
);
|
|
839
839
|
|
|
840
|
+
-- Preserve a second owner while exercising the invite creator's lost membership.
|
|
841
|
+
insert into agent_board.agents(id, agent_type, handle, display_name, status)
|
|
842
|
+
values ('16210000-0000-0000-0000-000000000002', 'agent', 'atomic-runtime-coowner', 'Atomic Runtime Coowner', 'active');
|
|
843
|
+
insert into agent_board.board_memberships(board_id, agent_id, membership_state, role)
|
|
844
|
+
select board_id, '16210000-0000-0000-0000-000000000002', 'joined', 'owner' from _atomic_cfg;
|
|
845
|
+
select agent_board._lock_board_authority_ceiling(board_id) from _atomic_cfg;
|
|
846
|
+
|
|
840
847
|
update agent_board.board_memberships
|
|
841
848
|
set membership_state='removed', left_at=now(), updated_at=now()
|
|
842
849
|
where board_id=(select board_id from _atomic_cfg)
|
|
@@ -14,7 +14,7 @@ set search_path = extensions, public, agent_board;
|
|
|
14
14
|
|
|
15
15
|
begin;
|
|
16
16
|
|
|
17
|
-
select plan(
|
|
17
|
+
select plan(69);
|
|
18
18
|
|
|
19
19
|
create function pg_temp.claims(p_sub text) returns void language plpgsql as $$
|
|
20
20
|
begin
|
|
@@ -215,9 +215,10 @@ select is(
|
|
|
215
215
|
-- reason. Keying only on INSERT would miss a future path that flips membership_state via
|
|
216
216
|
-- UPDATE -- and catching FUTURE writers is the entire purpose of this assertion.
|
|
217
217
|
--
|
|
218
|
-
-- The
|
|
219
|
-
-- appearing exclusively
|
|
220
|
-
-- change is
|
|
218
|
+
-- The exemptions are grant_board_role and set_board_member_label: each changes metadata
|
|
219
|
+
-- only, with membership_state appearing exclusively outside a SET clause, so neither can
|
|
220
|
+
-- change membership state and neither is a consent event. If that ever stops being true,
|
|
221
|
+
-- the positive assertion below fails.
|
|
221
222
|
-- COMMENTS ARE STRIPPED and CALL SITES ARE COUNTED. Checking that the emitter name appears
|
|
222
223
|
-- anywhere in the body is fail-open twice over: the name in a comment or a dead branch would
|
|
223
224
|
-- satisfy it, and calling the emitter TWICE would too -- while this suite claims exactly one
|
|
@@ -234,7 +235,12 @@ select is(
|
|
|
234
235
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
235
236
|
and agent_board_test.stripped_body(p.oid) ~ agent_board_test.stmt_re('insert into|update', 'board_memberships')
|
|
236
237
|
and agent_board_test.stripped_body(p.oid) !~* 'perform agent_board\._emit_membership_event'
|
|
237
|
-
|
|
238
|
+
-- Mixed SID writers change state only through the emitting sink; their
|
|
239
|
+
-- direct role-only UPDATE is not a second membership transition.
|
|
240
|
+
and (agent_board_test.stripped_body(p.oid) !~ agent_board_test.call_re('_upsert_board_membership_state', false)
|
|
241
|
+
or agent_board_test.stripped_body(p.oid) ~ agent_board_test.stmt_re('insert into', 'board_memberships')
|
|
242
|
+
or agent_board_test.stripped_body(p.oid) ~ agent_board_test.col_write_re('board_memberships', 'membership_state'))
|
|
243
|
+
and p.proname not in ('grant_board_role', 'set_board_member_label')),
|
|
238
244
|
'',
|
|
239
245
|
'COMPLETENESS: every function writing board_memberships emits, or is a stated exemption');
|
|
240
246
|
|
|
@@ -261,13 +267,26 @@ select is(
|
|
|
261
267
|
'',
|
|
262
268
|
'COMPLETENESS: no writer emits more than once (exactly one event per transition)');
|
|
263
269
|
|
|
264
|
-
-- ...and
|
|
265
|
-
select
|
|
266
|
-
(select
|
|
267
|
-
!~ agent_board_test.re(agent_board_test.col_assign_frag('board_memberships', 'membership_state', agent_board_test.bare_ok(p.oid)))
|
|
270
|
+
-- ...and each exemption is genuinely state-blind: membership_state never appears in a SET.
|
|
271
|
+
select is(
|
|
272
|
+
(select coalesce(string_agg(p.proname, ', ' order by p.proname), '')
|
|
268
273
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
269
|
-
where n.nspname = 'agent_board'
|
|
270
|
-
|
|
274
|
+
where n.nspname = 'agent_board'
|
|
275
|
+
and p.proname in ('grant_board_role', 'set_board_member_label')
|
|
276
|
+
and agent_board_test.stripped_body(p.oid, true)
|
|
277
|
+
!~ agent_board_test.re(agent_board_test.col_assign_frag('board_memberships', 'membership_state', agent_board_test.bare_ok(p.oid)))),
|
|
278
|
+
'grant_board_role, set_board_member_label',
|
|
279
|
+
'COMPLETENESS: each stated exemption is state-blind and never SETs membership_state');
|
|
280
|
+
|
|
281
|
+
select is(
|
|
282
|
+
(select string_agg(p.proname, ', ' order by p.proname)
|
|
283
|
+
from pg_proc p where p.pronamespace = 'agent_board'::regnamespace
|
|
284
|
+
and agent_board_test.stripped_body(p.oid) ~ agent_board_test.col_write_re('board_memberships', 'role')
|
|
285
|
+
and agent_board_test.stripped_body(p.oid) ~ agent_board_test.call_re('_upsert_board_membership_state', false)
|
|
286
|
+
and agent_board_test.stripped_body(p.oid) !~ agent_board_test.stmt_re('insert into', 'board_memberships')
|
|
287
|
+
and agent_board_test.stripped_body(p.oid) !~ agent_board_test.col_write_re('board_memberships', 'membership_state')),
|
|
288
|
+
'link_session_alias, mint_delegated_child, readmit_session_identity',
|
|
289
|
+
'COMPLETENESS: each mixed SID writer directly writes roles and delegates state transitions to the emitting sink');
|
|
271
290
|
|
|
272
291
|
-- ...and the emitter itself is the single implementation, not five copies.
|
|
273
292
|
select is(
|
|
@@ -17,7 +17,7 @@ set search_path = extensions, public, agent_board;
|
|
|
17
17
|
|
|
18
18
|
begin;
|
|
19
19
|
|
|
20
|
-
select plan(
|
|
20
|
+
select plan(55);
|
|
21
21
|
|
|
22
22
|
create function pg_temp.claims(p_sub text) returns void language plpgsql as $$
|
|
23
23
|
begin
|
|
@@ -350,11 +350,25 @@ select is(
|
|
|
350
350
|
'invite_board_member', -- writes `invited` only -- an offer, not a membership
|
|
351
351
|
'request_join_board', -- writes `requested` only
|
|
352
352
|
'leave_board', -- writes `left` only
|
|
353
|
-
'grant_board_role'
|
|
353
|
+
'grant_board_role', -- requires an already-active membership; never SETs the state
|
|
354
|
+
'readmit_session_identity' -- human owner mints a NEW principal; positively pinned below and behaviorally in SID readmission
|
|
354
355
|
)),
|
|
355
356
|
'',
|
|
356
357
|
'EC-4 CLASS: every authenticated route reaching an active membership is gated or exempt');
|
|
357
358
|
|
|
359
|
+
select ok(
|
|
360
|
+
agent_board_test.stripped_body('agent_board.readmit_session_identity(uuid,text,text,jsonb,jsonb,interval)'::regprocedure)
|
|
361
|
+
~ agent_board_test.call_re('_session_identity_owner', false)
|
|
362
|
+
and agent_board_test.stripped_body('agent_board.readmit_session_identity(uuid,text,text,jsonb,jsonb,interval)'::regprocedure)
|
|
363
|
+
~ agent_board_test.stmt_re('insert into', 'agents', false)
|
|
364
|
+
and agent_board_test.stripped_body('agent_board.readmit_session_identity(uuid,text,text,jsonb,jsonb,interval)'::regprocedure, true)
|
|
365
|
+
~ agent_board_test.re(agent_board_test.call_frag('_upsert_board_membership_state', false) || '\s*v_board\s*,\s*v_new_id\s*,\s*''joined''')
|
|
366
|
+
and agent_board_test.stripped_body('agent_board._session_identity_owner(uuid)'::regprocedure, true)
|
|
367
|
+
like '%human.agent_type = ''human''%'
|
|
368
|
+
and agent_board_test.stripped_body('agent_board._session_identity_owner(uuid)'::regprocedure)
|
|
369
|
+
like '%root.owner_user_id = auth.uid()%',
|
|
370
|
+
'EC-4-readmit: human-root-owner placement is onto a fresh principal, never manufactured target consent');
|
|
371
|
+
|
|
358
372
|
-- ...and the exemptions are not assertions of convenience: the three "writes only X" claims are
|
|
359
373
|
-- checked, because an exemption that stops being true is how the fifth route hid.
|
|
360
374
|
-- Matches the literal passed to EITHER the shared sink OR update_board_membership. The first
|
|
@@ -301,7 +301,7 @@ select f.oid, f.name,
|
|
|
301
301
|
-- 24
|
|
302
302
|
select is(
|
|
303
303
|
(select string_agg(name || case when writes_role then '[role]' else '' end, ',' order by name) from _bm_writers),
|
|
304
|
-
'_fedsession_autojoin_default_board[role],_redeem_unbounded_invite_for_agent[role],bootstrap_board_owner_memberships[role],create_board[role],grant_board_role[role]',
|
|
304
|
+
'_fedsession_autojoin_default_board[role],_redeem_unbounded_invite_for_agent[role],bootstrap_board_owner_memberships[role],create_board[role],grant_board_role[role],link_session_alias[role],mint_delegated_child[role],readmit_session_identity[role],set_board_member_label',
|
|
305
305
|
'the direct board_memberships writers outside the sink, derived (a new one names itself)');
|
|
306
306
|
-- 25
|
|
307
307
|
select is(
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
-- * REQUIRE DURABILITY (invariant 4): an operator require=true is not relaxed to false by
|
|
14
14
|
-- self-service (the self-pin), so a keyless self-service mint is refused — mutate the pin
|
|
15
15
|
-- (true -> coalesce(p_require,true)) and both the state read and the keyless-mint throw flip.
|
|
16
|
-
-- *
|
|
17
|
-
--
|
|
16
|
+
-- * SIDSCHEMA: both mint paths consume the tighter operator ceiling. The operator
|
|
17
|
+
-- can still set/loosen that ceiling through its existing writer path.
|
|
18
18
|
-- * NO REGRESSION: a root with no operator policy self-services bounded only by the global
|
|
19
19
|
-- 25/24h ceiling.
|
|
20
20
|
-- * EXECUTE class: CREATE OR REPLACE (no DROP) kept the ACL — mint stays service_role-only,
|
|
@@ -31,7 +31,11 @@ set search_path = extensions, public, agent_board;
|
|
|
31
31
|
|
|
32
32
|
begin;
|
|
33
33
|
|
|
34
|
-
select plan(
|
|
34
|
+
select plan(42);
|
|
35
|
+
create temp table _a_opcap(child_agent_id uuid, grant_expires_at timestamptz, signing_key_id uuid);
|
|
36
|
+
create temp table _b_ssttl(like _a_opcap);
|
|
37
|
+
create temp table _b_opttl(like _a_opcap);
|
|
38
|
+
create temp table _d_ss(like _a_opcap);
|
|
35
39
|
|
|
36
40
|
create temp table _cf (
|
|
37
41
|
owner_id uuid,
|
|
@@ -148,12 +152,19 @@ select throws_ok(
|
|
|
148
152
|
'42501', 'max_active_children: root has reached its active delegated-child ceiling',
|
|
149
153
|
'A: self-service mint clamps max_active_children to the operator ceiling of 1 (stored 25 NOT trusted)');
|
|
150
154
|
|
|
151
|
-
--
|
|
152
|
-
|
|
155
|
+
-- SIDSCHEMA's operator ceiling bounds BOTH mint paths, not merely self-service.
|
|
156
|
+
select throws_ok(
|
|
157
|
+
(select format($$ select * from agent_board.mint_delegated_child(%L::uuid, %L, '[]'::jsonb, %L, null, false) $$,
|
|
158
|
+
cap, 'cf-opfull-'||replace(substr(gen_random_uuid()::text,1,8),'-',''), k1) from _cf),
|
|
159
|
+
'42501', 'max_active_children: root has reached its active delegated-child ceiling',
|
|
160
|
+
'A: operator mint cannot bypass its tighter stored operator ceiling');
|
|
161
|
+
select is((select count(*) from _a_opcap), 0::bigint,
|
|
162
|
+
'A: operator ceiling refusal leaves no returned child');
|
|
163
|
+
update agent_board.delegation_grants set revoked_at = now() where child_agent_id = (select cap_child from _cf);
|
|
153
164
|
select lives_ok(
|
|
154
|
-
(select format($$
|
|
165
|
+
(select format($$ insert into _a_opcap select * from agent_board.mint_delegated_child(%L::uuid, %L, '[]'::jsonb, %L, null, false) $$,
|
|
155
166
|
cap, 'cf-opcap-'||replace(substr(gen_random_uuid()::text,1,8),'-',''), k1) from _cf),
|
|
156
|
-
'A: operator mint
|
|
167
|
+
'A: operator mint succeeds below the tighter ceiling (0 active grants < 1)');
|
|
157
168
|
select is((select count(*) from _a_opcap), 1::bigint,
|
|
158
169
|
'A: operator cap mint produced exactly one child (not a silent zero-row pass)');
|
|
159
170
|
|
|
@@ -185,21 +196,21 @@ select is((select child_ttl_ceiling from agent_board.delegation_policies where r
|
|
|
185
196
|
select set_config('request.jwt.claim.role', '', true);
|
|
186
197
|
select set_config('request.jwt.claim.sub', '', true);
|
|
187
198
|
select lives_ok(
|
|
188
|
-
(select format($$
|
|
199
|
+
(select format($$ insert into _b_ssttl select * from agent_board.mint_delegated_child(%L::uuid, %L, '[]'::jsonb, %L, null, true) $$,
|
|
189
200
|
ttl, 'cf-ssttl-'||replace(substr(gen_random_uuid()::text,1,8),'-',''), k2) from _cf),
|
|
190
201
|
'B: self-service mint under the 2h operator ceiling lives');
|
|
191
202
|
select ok(
|
|
192
203
|
(select grant_expires_at <= now() + interval '2 hours' + interval '1 minute' from _b_ssttl),
|
|
193
204
|
'B: self-service child TTL clamped to the 2h operator ceiling (stored 24h NOT trusted)');
|
|
194
205
|
|
|
195
|
-
--
|
|
206
|
+
-- The operator mint also consumes the durable 2h ceiling, per SIDSCHEMA's matrix.
|
|
196
207
|
select lives_ok(
|
|
197
|
-
(select format($$
|
|
208
|
+
(select format($$ insert into _b_opttl select * from agent_board.mint_delegated_child(%L::uuid, %L, '[]'::jsonb, %L, null, false) $$,
|
|
198
209
|
ttl, 'cf-opttl-'||replace(substr(gen_random_uuid()::text,1,8),'-',''), k3) from _cf),
|
|
199
210
|
'B: operator mint under the same policy lives');
|
|
200
211
|
select ok(
|
|
201
|
-
(select grant_expires_at
|
|
202
|
-
'B: operator
|
|
212
|
+
(select grant_expires_at <= now() + interval '2 hours' + interval '1 minute' from _b_opttl),
|
|
213
|
+
'B: operator mint consumes the same tighter 2h operator ceiling');
|
|
203
214
|
|
|
204
215
|
-- ===========================================================================
|
|
205
216
|
-- C. Operator require_child_signing_key=true is durable: self-service cannot relax it
|
|
@@ -251,7 +262,7 @@ select is((select operator_child_ttl_ceiling from agent_board.delegation_policie
|
|
|
251
262
|
select set_config('request.jwt.claim.role', '', true);
|
|
252
263
|
select set_config('request.jwt.claim.sub', '', true);
|
|
253
264
|
select lives_ok(
|
|
254
|
-
(select format($$
|
|
265
|
+
(select format($$ insert into _d_ss select * from agent_board.mint_delegated_child(%L::uuid, %L, '[]'::jsonb, %L, null, true) $$,
|
|
255
266
|
none, 'cf-ssnone-'||replace(substr(gen_random_uuid()::text,1,8),'-',''), k4) from _cf),
|
|
256
267
|
'D: self-service mint with no operator policy lives');
|
|
257
268
|
select ok(
|
|
@@ -284,7 +295,7 @@ select is((select scope_attestation from agent_board.delegation_policies where r
|
|
|
284
295
|
-- re-opened to PUBLIC/anon.
|
|
285
296
|
-- ===========================================================================
|
|
286
297
|
select ok(not has_function_privilege('authenticated',
|
|
287
|
-
'agent_board.mint_delegated_child(uuid, text, jsonb, text, interval, boolean)', 'EXECUTE'),
|
|
298
|
+
'agent_board.mint_delegated_child(uuid, text, jsonb, text, interval, boolean, text, text, text, jsonb, jsonb, uuid)', 'EXECUTE'),
|
|
288
299
|
'F: mint_delegated_child stays service_role-only (authenticated has NO EXECUTE after recreate)');
|
|
289
300
|
select ok(not has_function_privilege('anon',
|
|
290
301
|
'agent_board.upsert_delegation_policy(uuid, text, jsonb, integer, interval, boolean)', 'EXECUTE'),
|
|
@@ -293,7 +304,7 @@ select ok(has_function_privilege('authenticated',
|
|
|
293
304
|
'agent_board.upsert_delegation_policy(uuid, text, jsonb, integer, interval, boolean)', 'EXECUTE'),
|
|
294
305
|
'F: authenticated retains EXECUTE on upsert_delegation_policy after recreate');
|
|
295
306
|
select ok(has_function_privilege('service_role',
|
|
296
|
-
'agent_board.mint_delegated_child(uuid, text, jsonb, text, interval, boolean)', 'EXECUTE'),
|
|
307
|
+
'agent_board.mint_delegated_child(uuid, text, jsonb, text, interval, boolean, text, text, text, jsonb, jsonb, uuid)', 'EXECUTE'),
|
|
297
308
|
'F: service_role retains EXECUTE on mint_delegated_child');
|
|
298
309
|
|
|
299
310
|
select set_config('request.jwt.claim.role', '', true);
|
|
@@ -87,7 +87,7 @@ end
|
|
|
87
87
|
$$;
|
|
88
88
|
|
|
89
89
|
-- Function existence.
|
|
90
|
-
select has_function('agent_board', 'mint_delegated_child', array['uuid','text','jsonb','text','interval','boolean'], 'mint_delegated_child exists');
|
|
90
|
+
select has_function('agent_board', 'mint_delegated_child', array['uuid','text','jsonb','text','interval','boolean','text','text','text','jsonb','jsonb','uuid'], 'mint_delegated_child exists');
|
|
91
91
|
select has_function('agent_board', 'renew_delegated_grant', array['uuid','interval'], 'renew_delegated_grant exists');
|
|
92
92
|
|
|
93
93
|
-- Happy mint: grant row shape.
|
|
@@ -27,7 +27,7 @@ set search_path = extensions, public, agent_board;
|
|
|
27
27
|
|
|
28
28
|
begin;
|
|
29
29
|
|
|
30
|
-
select plan(
|
|
30
|
+
select plan(37);
|
|
31
31
|
|
|
32
32
|
-- ---------------------------------------------------------------------------
|
|
33
33
|
-- Fixtures
|
|
@@ -185,7 +185,10 @@ select is(
|
|
|
185
185
|
'_enroll_federated_identity_once, enroll_federated_identity, onboard_federated_fleet_agent',
|
|
186
186
|
'EC-1: exactly three agent_board routes accept an enrolled key fingerprint (pg_proc-derived)');
|
|
187
187
|
|
|
188
|
-
--
|
|
188
|
+
-- Readmission creates a fresh pending key for a verified human owner, never an
|
|
189
|
+
-- enrollment resumption. The exemption is positively checked below and exercised
|
|
190
|
+
-- with a real human owner and proof challenge in the SIDSCHEMA readmission suite.
|
|
191
|
+
-- The other creators still reach an enrolled key through the enroll wrapper, so
|
|
189
192
|
-- onboard_federated_fleet_agent necessarily reaches an already-enrolled key through
|
|
190
193
|
-- enroll_federated_identity rather than deciding the duplicate case itself.
|
|
191
194
|
select is(
|
|
@@ -201,8 +204,23 @@ select is(
|
|
|
201
204
|
-- invisible to the #196 lint because that lint only inspects POSITIONAL and
|
|
202
205
|
-- NEGATIVE assertions, and this is a PRESENCE match -- see the note on the lint.
|
|
203
206
|
and agent_board_test.stripped_body(p.oid) ~ agent_board_test.stmt_re('insert into', 'agent_signing_keys')),
|
|
204
|
-
'_enroll_federated_identity_once, mint_delegated_child',
|
|
205
|
-
'EC-2: only
|
|
207
|
+
'_enroll_federated_identity_once, mint_delegated_child, readmit_session_identity',
|
|
208
|
+
'EC-2: only enroll, delegated-child mint and fresh readmission insert signing keys (strip_sql-derived)');
|
|
209
|
+
select is(
|
|
210
|
+
(select string_agg(r || '=' || has_function_privilege(r, p.oid, 'EXECUTE')::text, ' ' order by r)
|
|
211
|
+
from pg_proc p join pg_namespace n on n.oid = p.pronamespace,
|
|
212
|
+
unnest(array['anon', 'authenticated', 'public', 'service_role']) r
|
|
213
|
+
where n.nspname = 'agent_board' and p.proname = 'readmit_session_identity'),
|
|
214
|
+
'anon=false authenticated=true public=false service_role=false',
|
|
215
|
+
'EC-2: readmission is human-authenticated only, not a service enrollment route');
|
|
216
|
+
select ok(
|
|
217
|
+
(select count(*) = 1 and bool_and(
|
|
218
|
+
agent_board_test.stripped_body(p.oid) ~ agent_board_test.call_re('_session_identity_owner')
|
|
219
|
+
-- Keep literals only because these two key-state values are the markers.
|
|
220
|
+
and agent_board_test.stripped_body(p.oid, true) ~ $$'pending_proof'\s*,\s*'unproven'$$)
|
|
221
|
+
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
222
|
+
where n.nspname = 'agent_board' and p.proname = 'readmit_session_identity'),
|
|
223
|
+
'EC-2: readmission positively verifies the owner and writes a pending, unproven fresh key');
|
|
206
224
|
|
|
207
225
|
-- ---------------------------------------------------------------------------
|
|
208
226
|
-- R-1 -- THE #176 FIX: an unstamped enrollment resumes for its owner
|
|
@@ -19,7 +19,7 @@ set search_path = extensions, public, agent_board;
|
|
|
19
19
|
|
|
20
20
|
begin;
|
|
21
21
|
|
|
22
|
-
select plan(
|
|
22
|
+
select plan(68);
|
|
23
23
|
|
|
24
24
|
-- ---------------------------------------------------------------------------
|
|
25
25
|
-- Fixtures
|
|
@@ -452,8 +452,23 @@ select is(
|
|
|
452
452
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
453
453
|
where n.nspname = 'agent_board' and p.prokind = 'f'
|
|
454
454
|
and agent_board_test.stripped_body(p.oid) ~ agent_board_test.stmt_re('insert into', 'agent_signing_keys')),
|
|
455
|
-
'_enroll_federated_identity_once, mint_delegated_child',
|
|
456
|
-
'EC-T2: exactly
|
|
455
|
+
'_enroll_federated_identity_once, mint_delegated_child, readmit_session_identity',
|
|
456
|
+
'EC-T2: exactly three functions insert signing keys, including fresh readmission (the EC-2 form)');
|
|
457
|
+
select is(
|
|
458
|
+
(select string_agg(r || '=' || has_function_privilege(r, p.oid, 'EXECUTE')::text, ' ' order by r)
|
|
459
|
+
from pg_proc p join pg_namespace n on n.oid = p.pronamespace,
|
|
460
|
+
unnest(array['anon', 'authenticated', 'public', 'service_role']) r
|
|
461
|
+
where n.nspname = 'agent_board' and p.proname = 'readmit_session_identity'),
|
|
462
|
+
'anon=false authenticated=true public=false service_role=false',
|
|
463
|
+
'EC-T2: readmission is human-authenticated only, not a Portal-token enrollment route');
|
|
464
|
+
select ok(
|
|
465
|
+
(select count(*) = 1 and bool_and(
|
|
466
|
+
agent_board_test.stripped_body(p.oid) ~ agent_board_test.call_re('_session_identity_owner')
|
|
467
|
+
-- Keep literals only because these two key-state values are the markers.
|
|
468
|
+
and agent_board_test.stripped_body(p.oid, true) ~ $$'pending_proof'\s*,\s*'unproven'$$)
|
|
469
|
+
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
470
|
+
where n.nspname = 'agent_board' and p.proname = 'readmit_session_identity'),
|
|
471
|
+
'EC-T2: readmission positively verifies the owner and writes a pending, unproven fresh key');
|
|
457
472
|
select is(
|
|
458
473
|
(select string_agg(r || '=' || has_function_privilege(r, p.oid, 'EXECUTE')::text, ' ' order by r)
|
|
459
474
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace,
|
|
@@ -233,8 +233,8 @@ select is(
|
|
|
233
233
|
(select string_agg(split_part(sig, '(', 1), ', ' order by split_part(sig, '(', 1))
|
|
234
234
|
from _lockclass where reaches_board and reaches_owner),
|
|
235
235
|
'_enroll_federated_human_after_revocation_gate, _enroll_federated_identity_once, '
|
|
236
|
-
|| 'enroll_federated_human, enroll_federated_identity, onboard_federated_fleet_agent',
|
|
237
|
-
'EC-2: the board+owner lock class
|
|
236
|
+
|| 'enroll_federated_human, enroll_federated_identity, mint_delegated_child, onboard_federated_fleet_agent, readmit_session_identity',
|
|
237
|
+
'EC-2: the board+owner lock class includes the federated routes and both SID mints');
|
|
238
238
|
|
|
239
239
|
-- EC-3: THE ORDERING INVARIANT. Never acquire auth.users before boards. The failure
|
|
240
240
|
-- prints the offending signatures (pre-fix this named enroll_federated_identity and
|