agent-passport-system 2.4.0-alpha → 2.5.0-alpha

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 (106) hide show
  1. package/README.md +9 -1
  2. package/dist/src/index.d.ts +2 -0
  3. package/dist/src/index.d.ts.map +1 -1
  4. package/dist/src/index.js +4 -0
  5. package/dist/src/index.js.map +1 -1
  6. package/dist/src/v2/accountability/__tests__/action.test.d.ts +2 -0
  7. package/dist/src/v2/accountability/__tests__/action.test.d.ts.map +1 -0
  8. package/dist/src/v2/accountability/__tests__/action.test.js +101 -0
  9. package/dist/src/v2/accountability/__tests__/action.test.js.map +1 -0
  10. package/dist/src/v2/accountability/__tests__/authority-boundary.test.d.ts +2 -0
  11. package/dist/src/v2/accountability/__tests__/authority-boundary.test.d.ts.map +1 -0
  12. package/dist/src/v2/accountability/__tests__/authority-boundary.test.js +79 -0
  13. package/dist/src/v2/accountability/__tests__/authority-boundary.test.js.map +1 -0
  14. package/dist/src/v2/accountability/__tests__/bundle.test.d.ts +2 -0
  15. package/dist/src/v2/accountability/__tests__/bundle.test.d.ts.map +1 -0
  16. package/dist/src/v2/accountability/__tests__/bundle.test.js +122 -0
  17. package/dist/src/v2/accountability/__tests__/bundle.test.js.map +1 -0
  18. package/dist/src/v2/accountability/__tests__/contestability.test.d.ts +2 -0
  19. package/dist/src/v2/accountability/__tests__/contestability.test.d.ts.map +1 -0
  20. package/dist/src/v2/accountability/__tests__/contestability.test.js +205 -0
  21. package/dist/src/v2/accountability/__tests__/contestability.test.js.map +1 -0
  22. package/dist/src/v2/accountability/__tests__/custody.test.d.ts +2 -0
  23. package/dist/src/v2/accountability/__tests__/custody.test.d.ts.map +1 -0
  24. package/dist/src/v2/accountability/__tests__/custody.test.js +155 -0
  25. package/dist/src/v2/accountability/__tests__/custody.test.js.map +1 -0
  26. package/dist/src/v2/accountability/__tests__/fixtures.test.d.ts +2 -0
  27. package/dist/src/v2/accountability/__tests__/fixtures.test.d.ts.map +1 -0
  28. package/dist/src/v2/accountability/__tests__/fixtures.test.js +48 -0
  29. package/dist/src/v2/accountability/__tests__/fixtures.test.js.map +1 -0
  30. package/dist/src/v2/accountability/construct/action.d.ts +7 -0
  31. package/dist/src/v2/accountability/construct/action.d.ts.map +1 -0
  32. package/dist/src/v2/accountability/construct/action.js +46 -0
  33. package/dist/src/v2/accountability/construct/action.js.map +1 -0
  34. package/dist/src/v2/accountability/construct/authority-boundary.d.ts +7 -0
  35. package/dist/src/v2/accountability/construct/authority-boundary.d.ts.map +1 -0
  36. package/dist/src/v2/accountability/construct/authority-boundary.js +34 -0
  37. package/dist/src/v2/accountability/construct/authority-boundary.js.map +1 -0
  38. package/dist/src/v2/accountability/construct/bundle.d.ts +19 -0
  39. package/dist/src/v2/accountability/construct/bundle.d.ts.map +1 -0
  40. package/dist/src/v2/accountability/construct/bundle.js +63 -0
  41. package/dist/src/v2/accountability/construct/bundle.js.map +1 -0
  42. package/dist/src/v2/accountability/construct/contestability.d.ts +9 -0
  43. package/dist/src/v2/accountability/construct/contestability.d.ts.map +1 -0
  44. package/dist/src/v2/accountability/construct/contestability.js +62 -0
  45. package/dist/src/v2/accountability/construct/contestability.js.map +1 -0
  46. package/dist/src/v2/accountability/construct/custody.d.ts +7 -0
  47. package/dist/src/v2/accountability/construct/custody.d.ts.map +1 -0
  48. package/dist/src/v2/accountability/construct/custody.js +41 -0
  49. package/dist/src/v2/accountability/construct/custody.js.map +1 -0
  50. package/dist/src/v2/accountability/index.d.ts +12 -0
  51. package/dist/src/v2/accountability/index.d.ts.map +1 -0
  52. package/dist/src/v2/accountability/index.js +37 -0
  53. package/dist/src/v2/accountability/index.js.map +1 -0
  54. package/dist/src/v2/accountability/types/action.d.ts +24 -0
  55. package/dist/src/v2/accountability/types/action.d.ts.map +1 -0
  56. package/dist/src/v2/accountability/types/action.js +11 -0
  57. package/dist/src/v2/accountability/types/action.js.map +1 -0
  58. package/dist/src/v2/accountability/types/authority-boundary.d.ts +16 -0
  59. package/dist/src/v2/accountability/types/authority-boundary.d.ts.map +1 -0
  60. package/dist/src/v2/accountability/types/authority-boundary.js +12 -0
  61. package/dist/src/v2/accountability/types/authority-boundary.js.map +1 -0
  62. package/dist/src/v2/accountability/types/base.d.ts +35 -0
  63. package/dist/src/v2/accountability/types/base.d.ts.map +1 -0
  64. package/dist/src/v2/accountability/types/base.js +10 -0
  65. package/dist/src/v2/accountability/types/base.js.map +1 -0
  66. package/dist/src/v2/accountability/types/bundle.d.ts +23 -0
  67. package/dist/src/v2/accountability/types/bundle.d.ts.map +1 -0
  68. package/dist/src/v2/accountability/types/bundle.js +12 -0
  69. package/dist/src/v2/accountability/types/bundle.js.map +1 -0
  70. package/dist/src/v2/accountability/types/contestability.d.ts +31 -0
  71. package/dist/src/v2/accountability/types/contestability.d.ts.map +1 -0
  72. package/dist/src/v2/accountability/types/contestability.js +18 -0
  73. package/dist/src/v2/accountability/types/contestability.js.map +1 -0
  74. package/dist/src/v2/accountability/types/custody.d.ts +18 -0
  75. package/dist/src/v2/accountability/types/custody.d.ts.map +1 -0
  76. package/dist/src/v2/accountability/types/custody.js +14 -0
  77. package/dist/src/v2/accountability/types/custody.js.map +1 -0
  78. package/dist/src/v2/accountability/types/index.d.ts +7 -0
  79. package/dist/src/v2/accountability/types/index.d.ts.map +1 -0
  80. package/dist/src/v2/accountability/types/index.js +7 -0
  81. package/dist/src/v2/accountability/types/index.js.map +1 -0
  82. package/dist/src/v2/accountability/verify/action.d.ts +8 -0
  83. package/dist/src/v2/accountability/verify/action.d.ts.map +1 -0
  84. package/dist/src/v2/accountability/verify/action.js +33 -0
  85. package/dist/src/v2/accountability/verify/action.js.map +1 -0
  86. package/dist/src/v2/accountability/verify/authority-boundary.d.ts +8 -0
  87. package/dist/src/v2/accountability/verify/authority-boundary.d.ts.map +1 -0
  88. package/dist/src/v2/accountability/verify/authority-boundary.js +26 -0
  89. package/dist/src/v2/accountability/verify/authority-boundary.js.map +1 -0
  90. package/dist/src/v2/accountability/verify/bundle.d.ts +8 -0
  91. package/dist/src/v2/accountability/verify/bundle.d.ts.map +1 -0
  92. package/dist/src/v2/accountability/verify/bundle.js +31 -0
  93. package/dist/src/v2/accountability/verify/bundle.js.map +1 -0
  94. package/dist/src/v2/accountability/verify/contestability.d.ts +8 -0
  95. package/dist/src/v2/accountability/verify/contestability.d.ts.map +1 -0
  96. package/dist/src/v2/accountability/verify/contestability.js +107 -0
  97. package/dist/src/v2/accountability/verify/contestability.js.map +1 -0
  98. package/dist/src/v2/accountability/verify/custody.d.ts +8 -0
  99. package/dist/src/v2/accountability/verify/custody.d.ts.map +1 -0
  100. package/dist/src/v2/accountability/verify/custody.js +58 -0
  101. package/dist/src/v2/accountability/verify/custody.js.map +1 -0
  102. package/dist/src/v2/index.d.ts +2 -0
  103. package/dist/src/v2/index.d.ts.map +1 -1
  104. package/dist/src/v2/index.js +4 -0
  105. package/dist/src/v2/index.js.map +1 -1
  106. package/package.json +3 -3
@@ -0,0 +1,205 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // ContestabilityReceipt — construct + verify conformance.
3
+ //
4
+ // Note: node:test rather than vitest because the repo has no vitest
5
+ // install; node:test is the v2 convention.
6
+ import { describe, it } from 'node:test';
7
+ import assert from 'node:assert/strict';
8
+ import { createHash } from 'node:crypto';
9
+ import { readFileSync } from 'node:fs';
10
+ import { dirname, join } from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { attachControllerResponse, createContestabilityReceipt, } from '../construct/contestability.js';
13
+ import { verifyContestabilityReceipt } from '../verify/contestability.js';
14
+ import { publicKeyFromPrivate } from '../../../crypto/keys.js';
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+ const CONTESTANT_PRIV = '33'.repeat(64);
17
+ const CONTROLLER_PRIV = '44'.repeat(64);
18
+ const CONTESTANT_TS = '2026-04-30T00:00:00.000Z';
19
+ const RESPONDED_TS = '2026-04-30T01:00:00.000Z';
20
+ const ACTION_ID = '0000000000000000000000000000000000000000000000000000000000000002';
21
+ const SCOPE = {
22
+ asserts: 'Named contestant filed a challenge against the named action with the named grounds.',
23
+ does_not_assert: [
24
+ 'that the contestation is meritorious',
25
+ 'that the standing is legally valid',
26
+ 'that any controller response is correct or final',
27
+ ],
28
+ capture_mode: 'self_attested',
29
+ completeness: 'complete',
30
+ self_attested: true,
31
+ };
32
+ function baseInput(overrides = {}) {
33
+ return {
34
+ timestamp: CONTESTANT_TS,
35
+ scope_of_claim: SCOPE,
36
+ contestant: {
37
+ did: 'did:aps:test-subject-001',
38
+ standing_basis: 'data_subject',
39
+ },
40
+ action_id: ACTION_ID,
41
+ grounds: 'Automated decision affected my access without disclosed criteria.',
42
+ requested_remedy: 'explanation',
43
+ ...overrides,
44
+ };
45
+ }
46
+ describe('ContestabilityReceipt — construct + verify', () => {
47
+ it('happy path without response: construct + verify round-trips green', () => {
48
+ const r = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
49
+ const v = verifyContestabilityReceipt(r);
50
+ assert.equal(v.valid, true, `expected valid; got reason=${v.reason}`);
51
+ assert.equal(r.claim_type, 'aps:contestability:v1');
52
+ assert.equal(r.controller_response, undefined);
53
+ });
54
+ it('receipt_id is stable across reruns with identical inputs', () => {
55
+ const a = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
56
+ const b = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
57
+ assert.equal(a.receipt_id, b.receipt_id);
58
+ assert.equal(a.signature, b.signature);
59
+ });
60
+ it('happy path with controller_response attached: both signatures verify', () => {
61
+ const filed = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
62
+ const controllerDid = publicKeyFromPrivate(CONTROLLER_PRIV);
63
+ const responded = attachControllerResponse(filed, {
64
+ status: 'under_review',
65
+ responded_at: RESPONDED_TS,
66
+ responder_did: controllerDid,
67
+ response_detail: 'Acknowledged. Routed to data-subject access team.',
68
+ }, CONTROLLER_PRIV);
69
+ assert.notEqual(responded.controller_response, undefined);
70
+ assert.equal(responded.controller_response?.response_signature.length, 128);
71
+ // Outer (contestant) signature is unchanged from the filing.
72
+ assert.equal(responded.signature, filed.signature);
73
+ assert.equal(responded.receipt_id, filed.receipt_id);
74
+ const v = verifyContestabilityReceipt(responded);
75
+ assert.equal(v.valid, true, `expected valid; got reason=${v.reason}`);
76
+ });
77
+ it('pseudonymous contestant: pseudonym_hash only, no did', () => {
78
+ const pseudo = createHash('sha256').update('subject-handle-42').digest('hex');
79
+ const r = createContestabilityReceipt(baseInput({
80
+ contestant: {
81
+ pseudonym_hash: pseudo,
82
+ standing_basis: 'data_subject',
83
+ },
84
+ }), CONTESTANT_PRIV);
85
+ assert.equal(r.contestant.did, undefined);
86
+ assert.equal(r.contestant.pseudonym_hash, pseudo);
87
+ const v = verifyContestabilityReceipt(r);
88
+ assert.equal(v.valid, true, `expected valid; got reason=${v.reason}`);
89
+ });
90
+ it('construct rejects contestant with neither did nor pseudonym_hash', () => {
91
+ assert.throws(() => createContestabilityReceipt(baseInput({
92
+ contestant: { standing_basis: 'data_subject' },
93
+ }), CONTESTANT_PRIV), /must have at least one of did or pseudonym_hash/);
94
+ });
95
+ it('verify reports MISSING_CONTESTANT_IDENTITY when both identity fields are empty', () => {
96
+ const r = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
97
+ const tampered = {
98
+ ...r,
99
+ contestant: { standing_basis: 'data_subject' },
100
+ };
101
+ const v = verifyContestabilityReceipt(tampered);
102
+ assert.equal(v.valid, false);
103
+ assert.equal(v.reason, 'MISSING_CONTESTANT_IDENTITY');
104
+ });
105
+ it('tamper on grounds field yields RECEIPT_ID_MISMATCH', () => {
106
+ const r = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
107
+ const tampered = {
108
+ ...r,
109
+ grounds: 'rewritten grounds after the fact',
110
+ };
111
+ const v = verifyContestabilityReceipt(tampered);
112
+ assert.equal(v.valid, false);
113
+ assert.equal(v.reason, 'RECEIPT_ID_MISMATCH');
114
+ });
115
+ it('tamper on response_detail invalidates the controller signature only', () => {
116
+ const filed = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
117
+ const controllerDid = publicKeyFromPrivate(CONTROLLER_PRIV);
118
+ const responded = attachControllerResponse(filed, {
119
+ status: 'under_review',
120
+ responded_at: RESPONDED_TS,
121
+ responder_did: controllerDid,
122
+ response_detail: 'original response detail',
123
+ }, CONTROLLER_PRIV);
124
+ const tampered = {
125
+ ...responded,
126
+ controller_response: {
127
+ ...responded.controller_response,
128
+ response_detail: 'rewritten response detail',
129
+ },
130
+ };
131
+ const v = verifyContestabilityReceipt(tampered);
132
+ assert.equal(v.valid, false);
133
+ assert.equal(v.reason, 'CONTROLLER_SIGNATURE_INVALID');
134
+ });
135
+ it('all 7 standing_basis values verify', () => {
136
+ const all = [
137
+ 'data_subject',
138
+ 'third_party',
139
+ 'regulator',
140
+ 'court',
141
+ 'internal_audit',
142
+ 'insurer',
143
+ 'principal',
144
+ ];
145
+ for (const standing_basis of all) {
146
+ const r = createContestabilityReceipt(baseInput({
147
+ contestant: { did: 'did:aps:test-subject-001', standing_basis },
148
+ }), CONTESTANT_PRIV);
149
+ const v = verifyContestabilityReceipt(r);
150
+ assert.equal(v.valid, true, `standing=${standing_basis} did not verify: ${v.reason}`);
151
+ }
152
+ });
153
+ it('all 6 requested_remedy values verify', () => {
154
+ const all = [
155
+ 'rollback',
156
+ 'review',
157
+ 'explanation',
158
+ 'compensation',
159
+ 'erasure',
160
+ 'modification',
161
+ ];
162
+ for (const requested_remedy of all) {
163
+ const r = createContestabilityReceipt(baseInput({ requested_remedy }), CONTESTANT_PRIV);
164
+ const v = verifyContestabilityReceipt(r);
165
+ assert.equal(v.valid, true, `remedy=${requested_remedy} did not verify: ${v.reason}`);
166
+ }
167
+ });
168
+ it('all 7 contest_status values verify when carried in controller_response', () => {
169
+ const all = [
170
+ 'filed',
171
+ 'under_review',
172
+ 'upheld',
173
+ 'rejected',
174
+ 'remedied',
175
+ 'expired',
176
+ 'abandoned',
177
+ ];
178
+ const controllerDid = publicKeyFromPrivate(CONTROLLER_PRIV);
179
+ for (const status of all) {
180
+ const filed = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
181
+ const responded = attachControllerResponse(filed, { status, responded_at: RESPONDED_TS, responder_did: controllerDid }, CONTROLLER_PRIV);
182
+ const v = verifyContestabilityReceipt(responded);
183
+ assert.equal(v.valid, true, `status=${status} did not verify: ${v.reason}`);
184
+ }
185
+ });
186
+ it('rejects wrong claim_type', () => {
187
+ const r = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
188
+ const tampered = {
189
+ ...r,
190
+ claim_type: 'aps:custody:v1',
191
+ };
192
+ const v = verifyContestabilityReceipt(tampered);
193
+ assert.equal(v.valid, false);
194
+ assert.equal(v.reason, 'INVALID_CLAIM_TYPE');
195
+ });
196
+ it('matches the on-disk fixture byte-for-byte (no controller_response)', () => {
197
+ const fixturePath = join(__dirname, '..', 'fixtures', 'contestability.fixture.json');
198
+ const fixture = JSON.parse(readFileSync(fixturePath, 'utf-8'));
199
+ const built = createContestabilityReceipt(baseInput(), CONTESTANT_PRIV);
200
+ assert.deepEqual(built, fixture);
201
+ const v = verifyContestabilityReceipt(fixture);
202
+ assert.equal(v.valid, true, `fixture failed verify: ${v.reason}`);
203
+ });
204
+ });
205
+ //# sourceMappingURL=contestability.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contestability.test.js","sourceRoot":"","sources":["../../../../../src/v2/accountability/__tests__/contestability.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=custody.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custody.test.d.ts","sourceRoot":"","sources":["../../../../../src/v2/accountability/__tests__/custody.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,155 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // CustodyReceipt — construct + verify conformance.
3
+ //
4
+ // Note: this file uses node:test rather than vitest because the repo
5
+ // has no vitest install; node:test is the v2 convention (see
6
+ // tests/v2/instruction-provenance/conformance.test.ts and siblings).
7
+ // The describe/it/assert API is interchangeable for these tests.
8
+ import { describe, it } from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import { readFileSync } from 'node:fs';
11
+ import { dirname, join } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { createCustodyReceipt } from '../construct/custody.js';
14
+ import { verifyCustodyReceipt } from '../verify/custody.js';
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+ const PRIV = '22'.repeat(64);
17
+ const TS = '2026-04-30T00:00:00.000Z';
18
+ const MERKLE_ROOT_1 = '0000000000000000000000000000000000000000000000000000000000000001';
19
+ const SCOPE = {
20
+ asserts: 'Custodian sealed the named receipt batch at the named time for the named purpose.',
21
+ does_not_assert: [
22
+ 'that the underlying receipts are factually accurate',
23
+ 'that downstream custody handoffs were honored',
24
+ ],
25
+ capture_mode: 'self_attested',
26
+ completeness: 'complete',
27
+ self_attested: true,
28
+ };
29
+ function makeBaseInput(overrides = {}) {
30
+ return {
31
+ timestamp: TS,
32
+ scope_of_claim: SCOPE,
33
+ custodian_did: 'did:aps:test-custodian-001',
34
+ event_type: 'sealed',
35
+ subject_receipt_batch: { merkle_root: MERKLE_ROOT_1, count: 1 },
36
+ purpose: 'internal_audit',
37
+ ...overrides,
38
+ };
39
+ }
40
+ describe('CustodyReceipt — construct + verify', () => {
41
+ it('happy path: construct + verify round-trips green', () => {
42
+ const r = createCustodyReceipt(makeBaseInput(), PRIV);
43
+ const v = verifyCustodyReceipt(r);
44
+ assert.equal(v.valid, true, `expected valid; got reason=${v.reason}`);
45
+ assert.equal(r.claim_type, 'aps:custody:v1');
46
+ assert.equal(r.signer_did.length, 64);
47
+ assert.equal(r.signature.length, 128);
48
+ assert.equal(r.receipt_id.length, 64);
49
+ });
50
+ it('receipt_id is stable across reruns with identical inputs', () => {
51
+ const a = createCustodyReceipt(makeBaseInput(), PRIV);
52
+ const b = createCustodyReceipt(makeBaseInput(), PRIV);
53
+ assert.equal(a.receipt_id, b.receipt_id);
54
+ assert.equal(a.signature, b.signature);
55
+ });
56
+ it('all 8 event types are accepted by construct + verify', () => {
57
+ const events = [
58
+ 'created',
59
+ 'sealed',
60
+ 'transferred',
61
+ 'disclosed',
62
+ 'redacted',
63
+ 'erased',
64
+ 'expired',
65
+ 'verified',
66
+ ];
67
+ for (const event_type of events) {
68
+ const input = makeBaseInput({ event_type });
69
+ // 'transferred' requires next_custodian_did per the type contract;
70
+ // handled in a dedicated test. Other events accept the base input.
71
+ if (event_type === 'transferred')
72
+ continue;
73
+ const r = createCustodyReceipt(input, PRIV);
74
+ const v = verifyCustodyReceipt(r);
75
+ assert.equal(v.valid, true, `event_type=${event_type} did not verify: ${v.reason}`);
76
+ }
77
+ });
78
+ it('all 7 purposes are accepted by construct + verify', () => {
79
+ const purposes = [
80
+ 'internal_audit',
81
+ 'regulator_disclosure',
82
+ 'subject_access',
83
+ 'litigation_discovery',
84
+ 'vendor_handoff',
85
+ 'archival',
86
+ 'incident_response',
87
+ ];
88
+ for (const purpose of purposes) {
89
+ const r = createCustodyReceipt(makeBaseInput({ purpose }), PRIV);
90
+ const v = verifyCustodyReceipt(r);
91
+ assert.equal(v.valid, true, `purpose=${purpose} did not verify: ${v.reason}`);
92
+ }
93
+ });
94
+ it('transferred event carries next_custodian_did and round-trips', () => {
95
+ const r = createCustodyReceipt(makeBaseInput({
96
+ event_type: 'transferred',
97
+ next_custodian_did: 'did:aps:test-custodian-002',
98
+ }), PRIV);
99
+ assert.equal(r.event_type, 'transferred');
100
+ assert.equal(r.next_custodian_did, 'did:aps:test-custodian-002');
101
+ const v = verifyCustodyReceipt(r);
102
+ assert.equal(v.valid, true, `verify failed: ${v.reason}`);
103
+ });
104
+ it('erased event preserves merkle_root + count (cryptographic-erasure pattern)', () => {
105
+ const r = createCustodyReceipt(makeBaseInput({
106
+ event_type: 'erased',
107
+ purpose: 'subject_access',
108
+ subject_receipt_batch: { merkle_root: MERKLE_ROOT_1, count: 42 },
109
+ }), PRIV);
110
+ assert.equal(r.event_type, 'erased');
111
+ // The chain remains verifiable across erasure: merkle_root and count
112
+ // survive even though the underlying content is irrecoverable.
113
+ assert.equal(r.subject_receipt_batch.merkle_root, MERKLE_ROOT_1);
114
+ assert.equal(r.subject_receipt_batch.count, 42);
115
+ const v = verifyCustodyReceipt(r);
116
+ assert.equal(v.valid, true, `verify failed: ${v.reason}`);
117
+ });
118
+ it('tamper on event_type to a non-enum value yields INVALID_EVENT_TYPE', () => {
119
+ const r = createCustodyReceipt(makeBaseInput(), PRIV);
120
+ const tampered = { ...r, event_type: 'fabricated' };
121
+ const v = verifyCustodyReceipt(tampered);
122
+ assert.equal(v.valid, false);
123
+ assert.equal(v.reason, 'INVALID_EVENT_TYPE');
124
+ });
125
+ it('tamper on event_type to a different valid enum yields RECEIPT_ID_MISMATCH', () => {
126
+ const r = createCustodyReceipt(makeBaseInput({ event_type: 'sealed' }), PRIV);
127
+ const tampered = { ...r, event_type: 'erased' };
128
+ const v = verifyCustodyReceipt(tampered);
129
+ assert.equal(v.valid, false);
130
+ assert.equal(v.reason, 'RECEIPT_ID_MISMATCH');
131
+ });
132
+ it('tamper on signature alone yields SIGNATURE_INVALID', () => {
133
+ const r = createCustodyReceipt(makeBaseInput(), PRIV);
134
+ const tampered = { ...r, signature: '00'.repeat(64) };
135
+ const v = verifyCustodyReceipt(tampered);
136
+ assert.equal(v.valid, false);
137
+ assert.equal(v.reason, 'SIGNATURE_INVALID');
138
+ });
139
+ it('rejects wrong claim_type', () => {
140
+ const r = createCustodyReceipt(makeBaseInput(), PRIV);
141
+ const tampered = { ...r, claim_type: 'aps:action:v1' };
142
+ const v = verifyCustodyReceipt(tampered);
143
+ assert.equal(v.valid, false);
144
+ assert.equal(v.reason, 'INVALID_CLAIM_TYPE');
145
+ });
146
+ it('matches the on-disk fixture byte-for-byte', () => {
147
+ const fixturePath = join(__dirname, '..', 'fixtures', 'custody.fixture.json');
148
+ const fixture = JSON.parse(readFileSync(fixturePath, 'utf-8'));
149
+ const built = createCustodyReceipt(makeBaseInput(), PRIV);
150
+ assert.deepEqual(built, fixture);
151
+ const v = verifyCustodyReceipt(fixture);
152
+ assert.equal(v.valid, true, `fixture failed verify: ${v.reason}`);
153
+ });
154
+ });
155
+ //# sourceMappingURL=custody.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custody.test.js","sourceRoot":"","sources":["../../../../../src/v2/accountability/__tests__/custody.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fixtures.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.test.d.ts","sourceRoot":"","sources":["../../../../../src/v2/accountability/__tests__/fixtures.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,48 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // Fixture cross-verification — every checked-in fixture must verify clean.
3
+ // Cross-implementation byte-match anchor: other SDKs validate against
4
+ // THESE files. Any change to construct/* that breaks fixture signatures
5
+ // fails this test loud.
6
+ import { describe, it } from 'node:test';
7
+ import assert from 'node:assert/strict';
8
+ import { readFileSync } from 'node:fs';
9
+ import { dirname, join } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { verifyActionReceipt } from '../verify/action.js';
12
+ import { verifyAuthorityBoundaryReceipt } from '../verify/authority-boundary.js';
13
+ import { verifyCustodyReceipt } from '../verify/custody.js';
14
+ import { verifyContestabilityReceipt } from '../verify/contestability.js';
15
+ import { verifyAPSBundle } from '../verify/bundle.js';
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ const FIXTURES_DIR = join(__dirname, '..', 'fixtures');
18
+ function loadFixture(name) {
19
+ return JSON.parse(readFileSync(join(FIXTURES_DIR, name), 'utf8'));
20
+ }
21
+ describe('accountability fixtures', () => {
22
+ it('action.fixture.json verifies clean', () => {
23
+ const r = loadFixture('action.fixture.json');
24
+ const v = verifyActionReceipt(r);
25
+ assert.equal(v.valid, true, JSON.stringify(v));
26
+ });
27
+ it('authority-boundary.fixture.json verifies clean', () => {
28
+ const r = loadFixture('authority-boundary.fixture.json');
29
+ const v = verifyAuthorityBoundaryReceipt(r);
30
+ assert.equal(v.valid, true, JSON.stringify(v));
31
+ });
32
+ it('custody.fixture.json verifies clean', () => {
33
+ const r = loadFixture('custody.fixture.json');
34
+ const v = verifyCustodyReceipt(r);
35
+ assert.equal(v.valid, true, JSON.stringify(v));
36
+ });
37
+ it('contestability.fixture.json verifies clean', () => {
38
+ const r = loadFixture('contestability.fixture.json');
39
+ const v = verifyContestabilityReceipt(r);
40
+ assert.equal(v.valid, true, JSON.stringify(v));
41
+ });
42
+ it('bundle.fixture.json verifies clean', () => {
43
+ const r = loadFixture('bundle.fixture.json');
44
+ const v = verifyAPSBundle(r);
45
+ assert.equal(v.valid, true, JSON.stringify(v));
46
+ });
47
+ });
48
+ //# sourceMappingURL=fixtures.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.test.js","sourceRoot":"","sources":["../../../../../src/v2/accountability/__tests__/fixtures.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ActionReceipt } from '../types/action.js';
2
+ export type CreateActionReceiptInput = Omit<ActionReceipt, 'receipt_id' | 'signature' | 'signer_did' | 'timestamp' | 'claim_type'> & {
3
+ /** Optional override; defaults to new Date().toISOString(). */
4
+ timestamp?: string;
5
+ };
6
+ export declare function createActionReceipt(input: CreateActionReceiptInput, signerPrivateKey: string): ActionReceipt;
7
+ //# sourceMappingURL=action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../../../src/v2/accountability/construct/action.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,aAAa,EACb,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,CACvE,GAAG;IACF,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,wBAAwB,EAC/B,gBAAgB,EAAE,MAAM,GACvB,aAAa,CAoCf"}
@@ -0,0 +1,46 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // ══════════════════════════════════════════════════════════════════
3
+ // ActionReceipt — construction
4
+ // ══════════════════════════════════════════════════════════════════
5
+ // receipt_id is the sha256 hex of JCS(receipt with empty receipt_id and
6
+ // empty signature). signature is Ed25519 over JCS(receipt with computed
7
+ // receipt_id and empty signature) — i.e. the signature covers the
8
+ // receipt_id, so any post-signing receipt_id tampering breaks the sig.
9
+ // ══════════════════════════════════════════════════════════════════
10
+ import { createHash } from 'node:crypto';
11
+ import { canonicalizeJCS } from '../../../core/canonical-jcs.js';
12
+ import { sign, publicKeyFromPrivate } from '../../../crypto/keys.js';
13
+ export function createActionReceipt(input, signerPrivateKey) {
14
+ const signer_did = publicKeyFromPrivate(signerPrivateKey);
15
+ const timestamp = input.timestamp ?? new Date().toISOString();
16
+ // Pre-signature draft: receipt_id and signature both empty.
17
+ const draft = {
18
+ claim_type: 'aps:action:v1',
19
+ receipt_id: '',
20
+ timestamp,
21
+ signer_did,
22
+ scope_of_claim: input.scope_of_claim,
23
+ agent_did: input.agent_did,
24
+ delegation_chain_root: input.delegation_chain_root,
25
+ ...(input.intent_ref !== undefined ? { intent_ref: input.intent_ref } : {}),
26
+ ...(input.policy_ref !== undefined ? { policy_ref: input.policy_ref } : {}),
27
+ action: input.action,
28
+ side_effect_classes: input.side_effect_classes,
29
+ ...(input.transparency_log_inclusion !== undefined
30
+ ? { transparency_log_inclusion: input.transparency_log_inclusion }
31
+ : {}),
32
+ ...(input.rfc3161_timestamp !== undefined
33
+ ? { rfc3161_timestamp: input.rfc3161_timestamp }
34
+ : {}),
35
+ signature: '',
36
+ };
37
+ // receipt_id = sha256(JCS(draft with empty receipt_id and empty signature))
38
+ const receiptIdBytes = canonicalizeJCS(draft);
39
+ const receipt_id = createHash('sha256').update(receiptIdBytes, 'utf8').digest('hex');
40
+ // signature = Ed25519(JCS(draft with computed receipt_id and empty signature))
41
+ const signed = { ...draft, receipt_id };
42
+ const signatureInput = canonicalizeJCS(signed);
43
+ const signature = sign(signatureInput, signerPrivateKey);
44
+ return { ...signed, signature };
45
+ }
46
+ //# sourceMappingURL=action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.js","sourceRoot":"","sources":["../../../../../src/v2/accountability/construct/action.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,qEAAqE;AACrE,+BAA+B;AAC/B,qEAAqE;AACrE,wEAAwE;AACxE,wEAAwE;AACxE,kEAAkE;AAClE,uEAAuE;AACvE,qEAAqE;AAErE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAWpE,MAAM,UAAU,mBAAmB,CACjC,KAA+B,EAC/B,gBAAwB;IAExB,MAAM,UAAU,GAAG,oBAAoB,CAAC,gBAAgB,CAAC,CAAA;IACzD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAE7D,4DAA4D;IAC5D,MAAM,KAAK,GAAkB;QAC3B,UAAU,EAAE,eAAe;QAC3B,UAAU,EAAE,EAAE;QACd,SAAS;QACT,UAAU;QACV,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;QAClD,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;QAC9C,GAAG,CAAC,KAAK,CAAC,0BAA0B,KAAK,SAAS;YAChD,CAAC,CAAC,EAAE,0BAA0B,EAAE,KAAK,CAAC,0BAA0B,EAAE;YAClE,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,KAAK,CAAC,iBAAiB,KAAK,SAAS;YACvC,CAAC,CAAC,EAAE,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,EAAE;YAChD,CAAC,CAAC,EAAE,CAAC;QACP,SAAS,EAAE,EAAE;KACd,CAAA;IAED,4EAA4E;IAC5E,MAAM,cAAc,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAEpF,+EAA+E;IAC/E,MAAM,MAAM,GAAkB,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,CAAA;IACtD,MAAM,cAAc,GAAG,eAAe,CAAC,MAAM,CAAC,CAAA;IAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAA;IAExD,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,CAAA;AACjC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { AuthorityBoundaryReceipt } from '../types/authority-boundary.js';
2
+ export type CreateAuthorityBoundaryReceiptInput = Omit<AuthorityBoundaryReceipt, 'receipt_id' | 'signature' | 'signer_did' | 'timestamp' | 'claim_type'> & {
3
+ /** Optional override; defaults to new Date().toISOString(). */
4
+ timestamp?: string;
5
+ };
6
+ export declare function createAuthorityBoundaryReceipt(input: CreateAuthorityBoundaryReceiptInput, evaluatorPrivateKey: string): AuthorityBoundaryReceipt;
7
+ //# sourceMappingURL=authority-boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authority-boundary.d.ts","sourceRoot":"","sources":["../../../../../src/v2/accountability/construct/authority-boundary.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAA;AAE9E,MAAM,MAAM,mCAAmC,GAAG,IAAI,CACpD,wBAAwB,EACxB,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,CACvE,GAAG;IACF,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,mCAAmC,EAC1C,mBAAmB,EAAE,MAAM,GAC1B,wBAAwB,CA0B1B"}
@@ -0,0 +1,34 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // ══════════════════════════════════════════════════════════════════
3
+ // AuthorityBoundaryReceipt — construction
4
+ // ══════════════════════════════════════════════════════════════════
5
+ // Same id-then-signature ordering as ActionReceipt: receipt_id covers
6
+ // the empty-id form, signature covers the populated-id form.
7
+ // ══════════════════════════════════════════════════════════════════
8
+ import { createHash } from 'node:crypto';
9
+ import { canonicalizeJCS } from '../../../core/canonical-jcs.js';
10
+ import { sign, publicKeyFromPrivate } from '../../../crypto/keys.js';
11
+ export function createAuthorityBoundaryReceipt(input, evaluatorPrivateKey) {
12
+ const signer_did = publicKeyFromPrivate(evaluatorPrivateKey);
13
+ const timestamp = input.timestamp ?? new Date().toISOString();
14
+ const draft = {
15
+ claim_type: 'aps:authority_boundary:v1',
16
+ receipt_id: '',
17
+ timestamp,
18
+ signer_did,
19
+ scope_of_claim: input.scope_of_claim,
20
+ action_id: input.action_id,
21
+ evaluator_did: input.evaluator_did,
22
+ delegation_chain_root: input.delegation_chain_root,
23
+ result: input.result,
24
+ ...(input.result_detail !== undefined ? { result_detail: input.result_detail } : {}),
25
+ signature: '',
26
+ };
27
+ const receiptIdBytes = canonicalizeJCS(draft);
28
+ const receipt_id = createHash('sha256').update(receiptIdBytes, 'utf8').digest('hex');
29
+ const signed = { ...draft, receipt_id };
30
+ const signatureInput = canonicalizeJCS(signed);
31
+ const signature = sign(signatureInput, evaluatorPrivateKey);
32
+ return { ...signed, signature };
33
+ }
34
+ //# sourceMappingURL=authority-boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authority-boundary.js","sourceRoot":"","sources":["../../../../../src/v2/accountability/construct/authority-boundary.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,qEAAqE;AACrE,0CAA0C;AAC1C,qEAAqE;AACrE,sEAAsE;AACtE,6DAA6D;AAC7D,qEAAqE;AAErE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAWpE,MAAM,UAAU,8BAA8B,CAC5C,KAA0C,EAC1C,mBAA2B;IAE3B,MAAM,UAAU,GAAG,oBAAoB,CAAC,mBAAmB,CAAC,CAAA;IAC5D,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAE7D,MAAM,KAAK,GAA6B;QACtC,UAAU,EAAE,2BAA2B;QACvC,UAAU,EAAE,EAAE;QACd,SAAS;QACT,UAAU;QACV,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;QAClD,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,GAAG,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpF,SAAS,EAAE,EAAE;KACd,CAAA;IAED,MAAM,cAAc,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAEpF,MAAM,MAAM,GAA6B,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,CAAA;IACjE,MAAM,cAAc,GAAG,eAAe,CAAC,MAAM,CAAC,CAAA;IAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAA;IAE3D,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,CAAA;AACjC,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { APSBundle, BundledReceiptRef } from '../types/bundle.js';
2
+ import type { ScopeOfClaim } from '../types/base.js';
3
+ /** Balanced binary Merkle tree over sorted receipt ids.
4
+ * Empty input returns sha256('') as the canonical sentinel.
5
+ * Odd-length layers duplicate the trailing element. */
6
+ export declare function computeMerkleRoot(receiptIds: string[]): string;
7
+ export interface CreateAPSBundleInput {
8
+ bundler_did: string;
9
+ period_start: string;
10
+ period_end: string;
11
+ subject_scope?: string[];
12
+ receipts: BundledReceiptRef[];
13
+ profile_conformance: string[];
14
+ scope_of_claim: ScopeOfClaim;
15
+ /** Optional override; defaults to new Date().toISOString(). */
16
+ timestamp?: string;
17
+ }
18
+ export declare function createAPSBundle(input: CreateAPSBundleInput, bundlerPrivateKey: string): APSBundle;
19
+ //# sourceMappingURL=bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../../../src/v2/accountability/construct/bundle.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAMpD;;wDAEwD;AACxD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAgB9D;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,EAAE,iBAAiB,EAAE,CAAA;IAC7B,mBAAmB,EAAE,MAAM,EAAE,CAAA;IAC7B,cAAc,EAAE,YAAY,CAAA;IAC5B,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,oBAAoB,EAC3B,iBAAiB,EAAE,MAAM,GACxB,SAAS,CA8BX"}