@mcp-i/core 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/auth/handshake.d.ts +19 -4
  2. package/dist/auth/handshake.d.ts.map +1 -1
  3. package/dist/auth/handshake.js +52 -15
  4. package/dist/auth/handshake.js.map +1 -1
  5. package/dist/auth/index.d.ts +1 -1
  6. package/dist/auth/index.d.ts.map +1 -1
  7. package/dist/auth/index.js.map +1 -1
  8. package/dist/delegation/cascading-revocation.d.ts.map +1 -1
  9. package/dist/delegation/cascading-revocation.js +3 -1
  10. package/dist/delegation/cascading-revocation.js.map +1 -1
  11. package/dist/delegation/did-key-resolver.d.ts.map +1 -1
  12. package/dist/delegation/did-key-resolver.js +9 -6
  13. package/dist/delegation/did-key-resolver.js.map +1 -1
  14. package/dist/delegation/outbound-headers.d.ts +14 -16
  15. package/dist/delegation/outbound-headers.d.ts.map +1 -1
  16. package/dist/delegation/outbound-headers.js +14 -15
  17. package/dist/delegation/outbound-headers.js.map +1 -1
  18. package/dist/delegation/outbound-proof.d.ts +1 -1
  19. package/dist/delegation/outbound-proof.js +1 -1
  20. package/dist/delegation/statuslist-manager.d.ts +3 -0
  21. package/dist/delegation/statuslist-manager.d.ts.map +1 -1
  22. package/dist/delegation/statuslist-manager.js +14 -1
  23. package/dist/delegation/statuslist-manager.js.map +1 -1
  24. package/dist/delegation/vc-verifier.d.ts.map +1 -1
  25. package/dist/delegation/vc-verifier.js +2 -2
  26. package/dist/delegation/vc-verifier.js.map +1 -1
  27. package/dist/errors.d.ts +42 -0
  28. package/dist/errors.d.ts.map +1 -0
  29. package/dist/errors.js +45 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/index.d.ts +3 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +3 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/middleware/index.d.ts +1 -0
  36. package/dist/middleware/index.d.ts.map +1 -1
  37. package/dist/middleware/index.js +1 -0
  38. package/dist/middleware/index.js.map +1 -1
  39. package/dist/middleware/mcpi-transport.d.ts +39 -0
  40. package/dist/middleware/mcpi-transport.d.ts.map +1 -0
  41. package/dist/middleware/mcpi-transport.js +121 -0
  42. package/dist/middleware/mcpi-transport.js.map +1 -0
  43. package/dist/middleware/with-mcpi-server.d.ts +25 -9
  44. package/dist/middleware/with-mcpi-server.d.ts.map +1 -1
  45. package/dist/middleware/with-mcpi-server.js +62 -47
  46. package/dist/middleware/with-mcpi-server.js.map +1 -1
  47. package/dist/middleware/with-mcpi.d.ts +40 -5
  48. package/dist/middleware/with-mcpi.d.ts.map +1 -1
  49. package/dist/middleware/with-mcpi.js +120 -10
  50. package/dist/middleware/with-mcpi.js.map +1 -1
  51. package/dist/providers/memory.js +2 -2
  52. package/dist/providers/memory.js.map +1 -1
  53. package/dist/session/manager.d.ts +7 -1
  54. package/dist/session/manager.d.ts.map +1 -1
  55. package/dist/session/manager.js +20 -4
  56. package/dist/session/manager.js.map +1 -1
  57. package/dist/utils/crypto-service.d.ts.map +1 -1
  58. package/dist/utils/crypto-service.js +11 -10
  59. package/dist/utils/crypto-service.js.map +1 -1
  60. package/dist/utils/did-helpers.d.ts +12 -0
  61. package/dist/utils/did-helpers.d.ts.map +1 -1
  62. package/dist/utils/did-helpers.js +18 -0
  63. package/dist/utils/did-helpers.js.map +1 -1
  64. package/package.json +2 -2
  65. package/src/__tests__/audit/canonicalization-integrity.test.ts +243 -0
  66. package/src/__tests__/audit/graph-revocation-roundtrip.test.ts +280 -0
  67. package/src/__tests__/audit/helpers/crypto-helpers.ts +245 -0
  68. package/src/__tests__/audit/proof-boundary.test.ts +269 -0
  69. package/src/__tests__/audit/statuslist-bitstring-roundtrip.test.ts +135 -0
  70. package/src/__tests__/audit/vc-roundtrip.test.ts +290 -0
  71. package/src/delegation/__tests__/outbound-headers.test.ts +16 -16
  72. package/src/delegation/__tests__/transitive-access.test.ts +1233 -0
  73. package/src/delegation/__tests__/vc-issuer.integration.test.ts +136 -0
  74. package/src/delegation/__tests__/vc-jwt.test.ts +318 -0
  75. package/src/delegation/__tests__/vc-verifier.integration.test.ts +199 -0
  76. package/src/delegation/cascading-revocation.ts +3 -1
  77. package/src/delegation/outbound-headers.ts +16 -16
  78. package/src/delegation/outbound-proof.ts +1 -1
  79. package/src/delegation/statuslist-manager.ts +17 -0
  80. package/src/middleware/with-mcpi.ts +29 -0
  81. package/src/proof/__tests__/verifier.integration.test.ts +181 -0
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Graph-Revocation Round-Trip Audit Tests
3
+ *
4
+ * Tests the delegation graph → cascading revocation pipeline with real
5
+ * StatusList2021 bitstring encoding/decoding and real gzip compression.
6
+ * No mocking of graph storage, status lists, or compression.
7
+ */
8
+
9
+ import { describe, it, expect, beforeEach, beforeAll } from 'vitest';
10
+ import { DelegationGraphManager } from '../../delegation/delegation-graph.js';
11
+ import { CascadingRevocationManager } from '../../delegation/cascading-revocation.js';
12
+ import type { AgentIdentity } from '../../providers/base.js';
13
+ import {
14
+ createRealCryptoProvider,
15
+ createRealIdentity,
16
+ createRealStatusListManager,
17
+ MemoryDelegationGraphStorage,
18
+ type RealStatusListSetup,
19
+ } from './helpers/crypto-helpers.js';
20
+ import { NodeCryptoProvider } from '../utils/node-crypto-provider.js';
21
+
22
+ describe('Graph-Revocation Round-Trip Audit', () => {
23
+ let crypto: NodeCryptoProvider;
24
+ let issuerIdentity: AgentIdentity;
25
+ let graph: DelegationGraphManager;
26
+ let graphStorage: MemoryDelegationGraphStorage;
27
+ let statusListSetup: RealStatusListSetup;
28
+ let revocationManager: CascadingRevocationManager;
29
+
30
+ // Create identities for chain: issuer -> agentA -> agentB -> agentC
31
+ let agentA: AgentIdentity;
32
+ let agentB: AgentIdentity;
33
+ let agentC: AgentIdentity;
34
+
35
+ beforeAll(async () => {
36
+ crypto = createRealCryptoProvider();
37
+ issuerIdentity = await createRealIdentity(crypto);
38
+ agentA = await createRealIdentity(crypto);
39
+ agentB = await createRealIdentity(crypto);
40
+ agentC = await createRealIdentity(crypto);
41
+ });
42
+
43
+ beforeEach(async () => {
44
+ graphStorage = new MemoryDelegationGraphStorage();
45
+ graph = new DelegationGraphManager(graphStorage);
46
+
47
+ statusListSetup = createRealStatusListManager(crypto, issuerIdentity, {
48
+ statusListBaseUrl: 'https://status.test.example.com',
49
+ });
50
+
51
+ revocationManager = new CascadingRevocationManager(graph, statusListSetup.manager);
52
+ });
53
+
54
+ // Helper: register a delegation and allocate a status entry
55
+ async function registerWithStatus(
56
+ id: string,
57
+ parentId: string | null,
58
+ issuerDid: string,
59
+ subjectDid: string
60
+ ): Promise<string> {
61
+ const statusEntry = await statusListSetup.manager.allocateStatusEntry('revocation');
62
+ await graph.registerDelegation({
63
+ id,
64
+ parentId,
65
+ issuerDid,
66
+ subjectDid,
67
+ credentialStatusId: statusEntry.id,
68
+ });
69
+ return statusEntry.id;
70
+ }
71
+
72
+ // ── Chain Validation ──────────────────────────────────────────
73
+
74
+ describe('chain validation with real graph', () => {
75
+ it('should validate a correctly chained delegation', async () => {
76
+ // issuer -> agentA -> agentB -> agentC
77
+ await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
78
+ await registerWithStatus('del-child', 'del-root', agentA.did, agentB.did);
79
+ await registerWithStatus('del-grandchild', 'del-child', agentB.did, agentC.did);
80
+
81
+ const result = await graph.validateChain('del-grandchild');
82
+ expect(result.valid).toBe(true);
83
+ });
84
+
85
+ it('should reject chain with issuer/subject mismatch', async () => {
86
+ // root: issuer -> agentA, child: agentC -> agentB (mismatch: agentC != agentA)
87
+ await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
88
+ await registerWithStatus('del-child', 'del-root', agentC.did, agentB.did);
89
+
90
+ const result = await graph.validateChain('del-child');
91
+ expect(result.valid).toBe(false);
92
+ expect(result.reason).toBeDefined();
93
+ });
94
+ });
95
+
96
+ // ── Cascading Revocation ──────────────────────────────────────
97
+
98
+ describe('cascading revocation with real StatusList2021', () => {
99
+ it('should revoke root and cascade to all descendants via StatusList', async () => {
100
+ const rootStatusId = await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
101
+ const childStatusId = await registerWithStatus('del-child', 'del-root', agentA.did, agentB.did);
102
+ const grandchildStatusId = await registerWithStatus('del-gc', 'del-child', agentB.did, agentC.did);
103
+
104
+ const events = await revocationManager.revokeDelegation('del-root');
105
+
106
+ expect(events.length).toBe(3);
107
+ expect(events[0]!.isRoot).toBe(true);
108
+
109
+ // Verify via real StatusList that all are revoked
110
+ const rootRevoked = await revocationManager.isRevoked('del-root');
111
+ const childRevoked = await revocationManager.isRevoked('del-child');
112
+ const gcRevoked = await revocationManager.isRevoked('del-gc');
113
+
114
+ expect(rootRevoked.revoked).toBe(true);
115
+ expect(childRevoked.revoked).toBe(true);
116
+ expect(gcRevoked.revoked).toBe(true);
117
+ });
118
+
119
+ it('should only revoke descendants, not siblings or ancestors', async () => {
120
+ await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
121
+ await registerWithStatus('del-child1', 'del-root', agentA.did, agentB.did);
122
+ await registerWithStatus('del-child2', 'del-root', agentA.did, agentC.did);
123
+
124
+ // Revoke child1 only
125
+ await revocationManager.revokeDelegation('del-child1');
126
+
127
+ const child1 = await revocationManager.isRevoked('del-child1');
128
+ const child2 = await revocationManager.isRevoked('del-child2');
129
+ const root = await revocationManager.isRevoked('del-root');
130
+
131
+ expect(child1.revoked).toBe(true);
132
+ expect(child2.revoked).toBe(false);
133
+ expect(root.revoked).toBe(false);
134
+ });
135
+
136
+ it('should detect ancestor revocation through chain walk', async () => {
137
+ await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
138
+ await registerWithStatus('del-child', 'del-root', agentA.did, agentB.did);
139
+
140
+ await revocationManager.revokeDelegation('del-root');
141
+
142
+ const childStatus = await revocationManager.isRevoked('del-child');
143
+ expect(childStatus.revoked).toBe(true);
144
+ expect(childStatus.revokedAncestor).toBe('del-root');
145
+ });
146
+ });
147
+
148
+ // ── Restore Behavior ──────────────────────────────────────────
149
+
150
+ describe('restore delegation', () => {
151
+ it('should restore root but NOT restore cascaded children', async () => {
152
+ await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
153
+ await registerWithStatus('del-child', 'del-root', agentA.did, agentB.did);
154
+
155
+ // Revoke root (cascades to child)
156
+ await revocationManager.revokeDelegation('del-root');
157
+
158
+ const rootBefore = await revocationManager.isRevoked('del-root');
159
+ const childBefore = await revocationManager.isRevoked('del-child');
160
+ expect(rootBefore.revoked).toBe(true);
161
+ expect(childBefore.revoked).toBe(true);
162
+
163
+ // Restore root only
164
+ await revocationManager.restoreDelegation('del-root');
165
+
166
+ const rootAfter = await revocationManager.isRevoked('del-root');
167
+ const childAfter = await revocationManager.isRevoked('del-child');
168
+
169
+ expect(rootAfter.revoked).toBe(false);
170
+ // Child should STILL be revoked — restore is not recursive
171
+ expect(childAfter.revoked).toBe(true);
172
+ });
173
+ });
174
+
175
+ // ── Dry-Run ───────────────────────────────────────────────────
176
+
177
+ describe('dry-run mode', () => {
178
+ it('should not modify StatusList bits in dry-run', async () => {
179
+ await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
180
+ await registerWithStatus('del-child', 'del-root', agentA.did, agentB.did);
181
+
182
+ const events = await revocationManager.revokeDelegation('del-root', { dryRun: true });
183
+
184
+ expect(events.length).toBe(2);
185
+
186
+ // Nothing should actually be revoked
187
+ const root = await revocationManager.isRevoked('del-root');
188
+ const child = await revocationManager.isRevoked('del-child');
189
+
190
+ expect(root.revoked).toBe(false);
191
+ expect(child.revoked).toBe(false);
192
+ });
193
+ });
194
+
195
+ // ── Deep Chain ────────────────────────────────────────────────
196
+
197
+ describe('deep chain cascading', () => {
198
+ it('should cascade through a depth-10 chain', async () => {
199
+ // Build chain of depth 10
200
+ const dids = [issuerIdentity, agentA, agentB, agentC];
201
+ let parentId: string | null = null;
202
+
203
+ for (let i = 0; i < 10; i++) {
204
+ const issuerIdx = i % dids.length;
205
+ const subjectIdx = (i + 1) % dids.length;
206
+ const id = `del-depth-${i}`;
207
+
208
+ await registerWithStatus(
209
+ id,
210
+ parentId,
211
+ dids[issuerIdx]!.did,
212
+ dids[subjectIdx]!.did
213
+ );
214
+ parentId = id;
215
+ }
216
+
217
+ // Revoke root — all 9 descendants should be revoked
218
+ const events = await revocationManager.revokeDelegation('del-depth-0');
219
+ expect(events.length).toBe(10);
220
+
221
+ for (let i = 0; i < 10; i++) {
222
+ const status = await revocationManager.isRevoked(`del-depth-${i}`);
223
+ expect(status.revoked).toBe(true);
224
+ }
225
+ });
226
+ });
227
+
228
+ // ── Concurrent Revocation ─────────────────────────────────────
229
+
230
+ describe('concurrent revocation', () => {
231
+ it('should handle concurrent revocation of sibling subtrees', async () => {
232
+ await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
233
+ await registerWithStatus('del-child1', 'del-root', agentA.did, agentB.did);
234
+ await registerWithStatus('del-gc1', 'del-child1', agentB.did, agentC.did);
235
+ await registerWithStatus('del-child2', 'del-root', agentA.did, agentC.did);
236
+ await registerWithStatus('del-gc2', 'del-child2', agentC.did, agentB.did);
237
+
238
+ // Revoke both subtrees concurrently
239
+ const [events1, events2] = await Promise.all([
240
+ revocationManager.revokeDelegation('del-child1'),
241
+ revocationManager.revokeDelegation('del-child2'),
242
+ ]);
243
+
244
+ expect(events1.length).toBeGreaterThanOrEqual(2);
245
+ expect(events2.length).toBeGreaterThanOrEqual(2);
246
+
247
+ // All 4 nodes should be revoked
248
+ for (const id of ['del-child1', 'del-gc1', 'del-child2', 'del-gc2']) {
249
+ const status = await revocationManager.isRevoked(id);
250
+ expect(status.revoked).toBe(true);
251
+ }
252
+
253
+ // Root should not be revoked
254
+ const root = await revocationManager.isRevoked('del-root');
255
+ expect(root.revoked).toBe(false);
256
+ });
257
+ });
258
+
259
+ // ── validateDelegation ────────────────────────────────────────
260
+
261
+ describe('validateDelegation', () => {
262
+ it('should return valid for non-revoked, well-formed chain', async () => {
263
+ await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
264
+ await registerWithStatus('del-child', 'del-root', agentA.did, agentB.did);
265
+
266
+ const result = await revocationManager.validateDelegation('del-child');
267
+ expect(result.valid).toBe(true);
268
+ });
269
+
270
+ it('should return invalid after ancestor revocation', async () => {
271
+ await registerWithStatus('del-root', null, issuerIdentity.did, agentA.did);
272
+ await registerWithStatus('del-child', 'del-root', agentA.did, agentB.did);
273
+
274
+ await revocationManager.revokeDelegation('del-root');
275
+
276
+ const result = await revocationManager.validateDelegation('del-child');
277
+ expect(result.valid).toBe(false);
278
+ });
279
+ });
280
+ });
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Shared Test Helpers for Audit Tests
3
+ *
4
+ * Provides real (non-mocked) crypto, clock, and signing utilities
5
+ * for round-trip and boundary testing. All helpers use NodeCryptoProvider
6
+ * for actual Ed25519 operations.
7
+ */
8
+
9
+ import * as zlib from 'node:zlib';
10
+ import { NodeCryptoProvider } from '../../utils/node-crypto-provider.js';
11
+ import { MemoryIdentityProvider, MemoryNonceCacheProvider } from '../../../providers/memory.js';
12
+ import { ClockProvider, FetchProvider } from '../../../providers/base.js';
13
+ import type { AgentIdentity } from '../../../providers/base.js';
14
+ import type { Proof, StatusList2021Credential, DelegationRecord } from '../../../types/protocol.js';
15
+ import type { VCSigningFunction } from '../../../delegation/vc-issuer.js';
16
+ import type { SignatureVerificationFunction, DIDDocument } from '../../../delegation/vc-verifier.js';
17
+ import { canonicalizeJSON } from '../../../delegation/utils.js';
18
+ import { createDidKeyResolver } from '../../../delegation/did-key-resolver.js';
19
+ import type { CompressionFunction, DecompressionFunction } from '../../../delegation/bitstring.js';
20
+ import { StatusList2021Manager } from '../../../delegation/statuslist-manager.js';
21
+ import { MemoryStatusListStorage } from '../../../delegation/storage/memory-statuslist-storage.js';
22
+
23
+ // ── Crypto ──────────────────────────────────────────────────────
24
+
25
+ export function createRealCryptoProvider(): NodeCryptoProvider {
26
+ return new NodeCryptoProvider();
27
+ }
28
+
29
+ export async function createRealIdentity(crypto: NodeCryptoProvider): Promise<AgentIdentity> {
30
+ const provider = new MemoryIdentityProvider(crypto);
31
+ return provider.getIdentity();
32
+ }
33
+
34
+ // ── Clock Providers ─────────────────────────────────────────────
35
+
36
+ export class RealClockProvider extends ClockProvider {
37
+ now(): number {
38
+ return Date.now();
39
+ }
40
+ isWithinSkew(timestampMs: number, skewSeconds: number): boolean {
41
+ return Math.abs(Date.now() - timestampMs) <= skewSeconds * 1000;
42
+ }
43
+ hasExpired(expiresAt: number): boolean {
44
+ return Date.now() > expiresAt;
45
+ }
46
+ calculateExpiry(ttlSeconds: number): number {
47
+ return Date.now() + ttlSeconds * 1000;
48
+ }
49
+ format(timestamp: number): string {
50
+ return new Date(timestamp).toISOString();
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Clock provider with a controllable "now" for precise boundary testing.
56
+ * `setNow(ms)` moves the clock to an exact millisecond.
57
+ */
58
+ export class ControllableClockProvider extends ClockProvider {
59
+ private currentMs: number;
60
+
61
+ constructor(nowMs: number = Date.now()) {
62
+ super();
63
+ this.currentMs = nowMs;
64
+ }
65
+
66
+ setNow(ms: number): void {
67
+ this.currentMs = ms;
68
+ }
69
+
70
+ advance(ms: number): void {
71
+ this.currentMs += ms;
72
+ }
73
+
74
+ now(): number {
75
+ return this.currentMs;
76
+ }
77
+
78
+ isWithinSkew(timestampMs: number, skewSeconds: number): boolean {
79
+ return Math.abs(this.currentMs - timestampMs) <= skewSeconds * 1000;
80
+ }
81
+
82
+ hasExpired(expiresAt: number): boolean {
83
+ return this.currentMs > expiresAt;
84
+ }
85
+
86
+ calculateExpiry(ttlSeconds: number): number {
87
+ return this.currentMs + ttlSeconds * 1000;
88
+ }
89
+
90
+ format(timestamp: number): string {
91
+ return new Date(timestamp).toISOString();
92
+ }
93
+ }
94
+
95
+ // ── Fetch Provider ──────────────────────────────────────────────
96
+
97
+ export class RealFetchProvider extends FetchProvider {
98
+ private didResolver = createDidKeyResolver();
99
+
100
+ async resolveDID(did: string): Promise<DIDDocument | null> {
101
+ return this.didResolver.resolve(did);
102
+ }
103
+
104
+ async fetchStatusList(_url: string): Promise<StatusList2021Credential | null> {
105
+ return null;
106
+ }
107
+
108
+ async fetchDelegationChain(_id: string): Promise<DelegationRecord[]> {
109
+ return [];
110
+ }
111
+
112
+ async fetch(_url: string, _options?: unknown): Promise<Response> {
113
+ throw new Error('Not implemented');
114
+ }
115
+ }
116
+
117
+ // ── Signing & Verification ──────────────────────────────────────
118
+
119
+ /**
120
+ * Creates a real VCSigningFunction that produces Ed25519Signature2020 proofs.
121
+ * The signature is over the canonicalized VC (the `canonicalVC` string passed in).
122
+ */
123
+ export function createRealSigningFunction(
124
+ crypto: NodeCryptoProvider,
125
+ identity: AgentIdentity
126
+ ): VCSigningFunction {
127
+ return async (canonicalVC: string, issuerDid: string, kid: string): Promise<Proof> => {
128
+ const data = new TextEncoder().encode(canonicalVC);
129
+ const signature = await crypto.sign(data, identity.privateKey);
130
+ const proofValue = Buffer.from(signature).toString('base64url');
131
+
132
+ return {
133
+ type: 'Ed25519Signature2020',
134
+ created: new Date().toISOString(),
135
+ verificationMethod: kid,
136
+ proofPurpose: 'assertionMethod',
137
+ proofValue,
138
+ };
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Creates a real SignatureVerificationFunction for the VC verifier.
144
+ * Strips `proof`, canonicalizes, and verifies the Ed25519 signature.
145
+ */
146
+ export function createRealSignatureVerifier(
147
+ crypto: NodeCryptoProvider
148
+ ): SignatureVerificationFunction {
149
+ return async (vc, publicKeyJwk) => {
150
+ try {
151
+ const jwk = publicKeyJwk as { x?: string };
152
+ if (!jwk.x) {
153
+ return { valid: false, reason: 'Missing public key x coordinate' };
154
+ }
155
+
156
+ // Decode the public key from base64url (JWK x parameter)
157
+ const publicKeyBase64 = Buffer.from(jwk.x, 'base64url').toString('base64');
158
+
159
+ // Strip proof and canonicalize
160
+ const vcWithoutProof = { ...vc } as Record<string, unknown>;
161
+ delete vcWithoutProof['proof'];
162
+ const canonicalVC = canonicalizeJSON(vcWithoutProof);
163
+ const data = new TextEncoder().encode(canonicalVC);
164
+
165
+ // Extract signature from proof
166
+ const proofValue = vc.proof?.proofValue;
167
+ if (!proofValue) {
168
+ return { valid: false, reason: 'Missing proofValue' };
169
+ }
170
+
171
+ const signature = Buffer.from(proofValue as string, 'base64url');
172
+ const isValid = await crypto.verify(data, new Uint8Array(signature), publicKeyBase64);
173
+
174
+ return { valid: isValid, reason: isValid ? undefined : 'Signature verification failed' };
175
+ } catch (error) {
176
+ return {
177
+ valid: false,
178
+ reason: `Verification error: ${error instanceof Error ? error.message : String(error)}`,
179
+ };
180
+ }
181
+ };
182
+ }
183
+
184
+ // ── Compression (real gzip via Node.js zlib) ────────────────────
185
+
186
+ export const nodeCompressor: CompressionFunction = {
187
+ compress(data: Uint8Array): Promise<Uint8Array> {
188
+ return new Promise((resolve, reject) => {
189
+ zlib.gzip(Buffer.from(data), (err, result) => {
190
+ if (err) reject(err);
191
+ else resolve(new Uint8Array(result));
192
+ });
193
+ });
194
+ },
195
+ };
196
+
197
+ export const nodeDecompressor: DecompressionFunction = {
198
+ decompress(data: Uint8Array): Promise<Uint8Array> {
199
+ return new Promise((resolve, reject) => {
200
+ zlib.gunzip(Buffer.from(data), (err, result) => {
201
+ if (err) reject(err);
202
+ else resolve(new Uint8Array(result));
203
+ });
204
+ });
205
+ },
206
+ };
207
+
208
+ // ── StatusList2021 Manager (real) ───────────────────────────────
209
+
210
+ export interface RealStatusListSetup {
211
+ manager: StatusList2021Manager;
212
+ storage: MemoryStatusListStorage;
213
+ }
214
+
215
+ export function createRealStatusListManager(
216
+ crypto: NodeCryptoProvider,
217
+ identity: AgentIdentity,
218
+ options?: { statusListBaseUrl?: string; defaultListSize?: number }
219
+ ): RealStatusListSetup {
220
+ const storage = new MemoryStatusListStorage();
221
+ const signingFunction = createRealSigningFunction(crypto, identity);
222
+
223
+ const identityProvider = {
224
+ getDid: () => identity.did,
225
+ getKeyId: () => identity.kid,
226
+ };
227
+
228
+ const manager = new StatusList2021Manager(
229
+ storage,
230
+ identityProvider,
231
+ signingFunction,
232
+ nodeCompressor,
233
+ nodeDecompressor,
234
+ options
235
+ );
236
+
237
+ return { manager, storage };
238
+ }
239
+
240
+ // ── Re-exports for convenience ──────────────────────────────────
241
+
242
+ export { MemoryNonceCacheProvider } from '../../../providers/memory.js';
243
+ export { MemoryIdentityProvider } from '../../../providers/memory.js';
244
+ export { MemoryDelegationGraphStorage } from '../../../delegation/storage/memory-graph-storage.js';
245
+ export { MemoryStatusListStorage } from '../../../delegation/storage/memory-statuslist-storage.js';