@kya-os/mcp-i-core 1.3.7-canary.0 → 1.3.7-canary.clientinfo.20251126041014

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 (236) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test$colon$coverage.log +4239 -0
  3. package/.turbo/turbo-test.log +2973 -0
  4. package/COMPLIANCE_IMPROVEMENT_REPORT.md +483 -0
  5. package/Composer 3.md +615 -0
  6. package/GPT-5.md +1169 -0
  7. package/OPUS-plan.md +352 -0
  8. package/PHASE_3_AND_4.1_SUMMARY.md +585 -0
  9. package/PHASE_3_SUMMARY.md +317 -0
  10. package/PHASE_4.1.3_SUMMARY.md +428 -0
  11. package/PHASE_4.1_COMPLETE.md +525 -0
  12. package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +1240 -0
  13. package/SCHEMA_COMPLIANCE_REPORT.md +275 -0
  14. package/TEST_PLAN.md +571 -0
  15. package/coverage/coverage-final.json +57 -0
  16. package/dist/__tests__/utils/mock-providers.d.ts +1 -2
  17. package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
  18. package/dist/__tests__/utils/mock-providers.js.map +1 -1
  19. package/dist/cache/oauth-config-cache.d.ts +69 -0
  20. package/dist/cache/oauth-config-cache.d.ts.map +1 -0
  21. package/dist/cache/oauth-config-cache.js +76 -0
  22. package/dist/cache/oauth-config-cache.js.map +1 -0
  23. package/dist/identity/idp-token-resolver.d.ts +53 -0
  24. package/dist/identity/idp-token-resolver.d.ts.map +1 -0
  25. package/dist/identity/idp-token-resolver.js +108 -0
  26. package/dist/identity/idp-token-resolver.js.map +1 -0
  27. package/dist/identity/idp-token-storage.interface.d.ts +42 -0
  28. package/dist/identity/idp-token-storage.interface.d.ts.map +1 -0
  29. package/dist/identity/idp-token-storage.interface.js +12 -0
  30. package/dist/identity/idp-token-storage.interface.js.map +1 -0
  31. package/dist/identity/user-did-manager.d.ts +39 -1
  32. package/dist/identity/user-did-manager.d.ts.map +1 -1
  33. package/dist/identity/user-did-manager.js +69 -3
  34. package/dist/identity/user-did-manager.js.map +1 -1
  35. package/dist/index.d.ts +24 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +43 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/runtime/audit-logger.d.ts +37 -0
  40. package/dist/runtime/audit-logger.d.ts.map +1 -0
  41. package/dist/runtime/audit-logger.js +9 -0
  42. package/dist/runtime/audit-logger.js.map +1 -0
  43. package/dist/runtime/base.d.ts +19 -2
  44. package/dist/runtime/base.d.ts.map +1 -1
  45. package/dist/runtime/base.js +227 -11
  46. package/dist/runtime/base.js.map +1 -1
  47. package/dist/services/access-control.service.d.ts.map +1 -1
  48. package/dist/services/access-control.service.js +199 -15
  49. package/dist/services/access-control.service.js.map +1 -1
  50. package/dist/services/authorization/authorization-registry.d.ts +29 -0
  51. package/dist/services/authorization/authorization-registry.d.ts.map +1 -0
  52. package/dist/services/authorization/authorization-registry.js +57 -0
  53. package/dist/services/authorization/authorization-registry.js.map +1 -0
  54. package/dist/services/authorization/types.d.ts +53 -0
  55. package/dist/services/authorization/types.d.ts.map +1 -0
  56. package/dist/services/authorization/types.js +10 -0
  57. package/dist/services/authorization/types.js.map +1 -0
  58. package/dist/services/batch-delegation.service.d.ts +53 -0
  59. package/dist/services/batch-delegation.service.d.ts.map +1 -0
  60. package/dist/services/batch-delegation.service.js +95 -0
  61. package/dist/services/batch-delegation.service.js.map +1 -0
  62. package/dist/services/index.d.ts +2 -0
  63. package/dist/services/index.d.ts.map +1 -1
  64. package/dist/services/index.js +4 -1
  65. package/dist/services/index.js.map +1 -1
  66. package/dist/services/oauth-config.service.d.ts +53 -0
  67. package/dist/services/oauth-config.service.d.ts.map +1 -0
  68. package/dist/services/oauth-config.service.js +141 -0
  69. package/dist/services/oauth-config.service.js.map +1 -0
  70. package/dist/services/oauth-provider-registry.d.ts +88 -0
  71. package/dist/services/oauth-provider-registry.d.ts.map +1 -0
  72. package/dist/services/oauth-provider-registry.js +128 -0
  73. package/dist/services/oauth-provider-registry.js.map +1 -0
  74. package/dist/services/oauth-service.d.ts +77 -0
  75. package/dist/services/oauth-service.d.ts.map +1 -0
  76. package/dist/services/oauth-service.js +373 -0
  77. package/dist/services/oauth-service.js.map +1 -0
  78. package/dist/services/oauth-token-retrieval.service.d.ts +49 -0
  79. package/dist/services/oauth-token-retrieval.service.d.ts.map +1 -0
  80. package/dist/services/oauth-token-retrieval.service.js +150 -0
  81. package/dist/services/oauth-token-retrieval.service.js.map +1 -0
  82. package/dist/services/provider-resolver.d.ts +48 -0
  83. package/dist/services/provider-resolver.d.ts.map +1 -0
  84. package/dist/services/provider-resolver.js +121 -0
  85. package/dist/services/provider-resolver.js.map +1 -0
  86. package/dist/services/provider-validator.d.ts +55 -0
  87. package/dist/services/provider-validator.d.ts.map +1 -0
  88. package/dist/services/provider-validator.js +135 -0
  89. package/dist/services/provider-validator.js.map +1 -0
  90. package/dist/services/session-registration.service.d.ts +80 -0
  91. package/dist/services/session-registration.service.d.ts.map +1 -0
  92. package/dist/services/session-registration.service.js +228 -0
  93. package/dist/services/session-registration.service.js.map +1 -0
  94. package/dist/services/tool-context-builder.d.ts +57 -0
  95. package/dist/services/tool-context-builder.d.ts.map +1 -0
  96. package/dist/services/tool-context-builder.js +125 -0
  97. package/dist/services/tool-context-builder.js.map +1 -0
  98. package/dist/services/tool-protection.service.d.ts +27 -0
  99. package/dist/services/tool-protection.service.d.ts.map +1 -1
  100. package/dist/services/tool-protection.service.js +194 -4
  101. package/dist/services/tool-protection.service.js.map +1 -1
  102. package/dist/types/oauth-required-error.d.ts +40 -0
  103. package/dist/types/oauth-required-error.d.ts.map +1 -0
  104. package/dist/types/oauth-required-error.js +40 -0
  105. package/dist/types/oauth-required-error.js.map +1 -0
  106. package/dist/utils/did-helpers.d.ts +33 -0
  107. package/dist/utils/did-helpers.d.ts.map +1 -1
  108. package/dist/utils/did-helpers.js +40 -0
  109. package/dist/utils/did-helpers.js.map +1 -1
  110. package/dist/utils/index.d.ts +1 -0
  111. package/dist/utils/index.d.ts.map +1 -1
  112. package/dist/utils/index.js +1 -0
  113. package/dist/utils/index.js.map +1 -1
  114. package/docs/API_REFERENCE.md +1362 -0
  115. package/docs/COMPLIANCE_MATRIX.md +691 -0
  116. package/docs/STATUSLIST2021_GUIDE.md +696 -0
  117. package/docs/W3C_VC_DELEGATION_GUIDE.md +710 -0
  118. package/package.json +23 -54
  119. package/scripts/audit-compliance.ts +724 -0
  120. package/src/__tests__/cache/tool-protection-cache.test.ts +640 -0
  121. package/src/__tests__/config/provider-runtime-config.test.ts +309 -0
  122. package/src/__tests__/delegation-e2e.test.ts +690 -0
  123. package/src/__tests__/identity/user-did-manager.test.ts +213 -0
  124. package/src/__tests__/index.test.ts +56 -0
  125. package/src/__tests__/integration/full-flow.test.ts +776 -0
  126. package/src/__tests__/integration.test.ts +281 -0
  127. package/src/__tests__/providers/base.test.ts +173 -0
  128. package/src/__tests__/providers/memory.test.ts +319 -0
  129. package/src/__tests__/regression/phase2-regression.test.ts +429 -0
  130. package/src/__tests__/runtime/audit-logger.test.ts +154 -0
  131. package/src/__tests__/runtime/base-extensions.test.ts +593 -0
  132. package/src/__tests__/runtime/base.test.ts +869 -0
  133. package/src/__tests__/runtime/delegation-flow.test.ts +164 -0
  134. package/src/__tests__/runtime/proof-client-did.test.ts +375 -0
  135. package/src/__tests__/runtime/route-interception.test.ts +686 -0
  136. package/src/__tests__/runtime/tool-protection-enforcement.test.ts +908 -0
  137. package/src/__tests__/services/agentshield-integration.test.ts +784 -0
  138. package/src/__tests__/services/cache-busting.test.ts +125 -0
  139. package/src/__tests__/services/oauth-service-pkce.test.ts +556 -0
  140. package/src/__tests__/services/provider-resolver-edge-cases.test.ts +591 -0
  141. package/src/__tests__/services/tool-protection-oauth-provider.test.ts +480 -0
  142. package/src/__tests__/services/tool-protection.service.test.ts +1366 -0
  143. package/src/__tests__/utils/mock-providers.ts +340 -0
  144. package/src/cache/oauth-config-cache.d.ts +69 -0
  145. package/src/cache/oauth-config-cache.d.ts.map +1 -0
  146. package/src/cache/oauth-config-cache.js.map +1 -0
  147. package/src/cache/oauth-config-cache.ts +123 -0
  148. package/src/cache/tool-protection-cache.ts +171 -0
  149. package/src/compliance/EXAMPLE.md +412 -0
  150. package/src/compliance/__tests__/schema-verifier.test.ts +797 -0
  151. package/src/compliance/index.ts +8 -0
  152. package/src/compliance/schema-registry.ts +460 -0
  153. package/src/compliance/schema-verifier.ts +708 -0
  154. package/src/config/__tests__/remote-config.spec.ts +268 -0
  155. package/src/config/remote-config.ts +174 -0
  156. package/src/config.ts +309 -0
  157. package/src/delegation/__tests__/audience-validator.test.ts +112 -0
  158. package/src/delegation/__tests__/bitstring.test.ts +346 -0
  159. package/src/delegation/__tests__/cascading-revocation.test.ts +628 -0
  160. package/src/delegation/__tests__/delegation-graph.test.ts +584 -0
  161. package/src/delegation/__tests__/utils.test.ts +152 -0
  162. package/src/delegation/__tests__/vc-issuer.test.ts +442 -0
  163. package/src/delegation/__tests__/vc-verifier.test.ts +922 -0
  164. package/src/delegation/audience-validator.ts +52 -0
  165. package/src/delegation/bitstring.ts +278 -0
  166. package/src/delegation/cascading-revocation.ts +370 -0
  167. package/src/delegation/delegation-graph.ts +299 -0
  168. package/src/delegation/index.ts +14 -0
  169. package/src/delegation/statuslist-manager.ts +353 -0
  170. package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
  171. package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
  172. package/src/delegation/storage/index.ts +9 -0
  173. package/src/delegation/storage/memory-graph-storage.ts +178 -0
  174. package/src/delegation/storage/memory-statuslist-storage.ts +77 -0
  175. package/src/delegation/utils.ts +42 -0
  176. package/src/delegation/vc-issuer.ts +232 -0
  177. package/src/delegation/vc-verifier.ts +568 -0
  178. package/src/identity/idp-token-resolver.ts +147 -0
  179. package/src/identity/idp-token-storage.interface.ts +59 -0
  180. package/src/identity/user-did-manager.ts +370 -0
  181. package/src/index.ts +271 -0
  182. package/src/providers/base.d.ts +91 -0
  183. package/src/providers/base.d.ts.map +1 -0
  184. package/src/providers/base.js.map +1 -0
  185. package/src/providers/base.ts +96 -0
  186. package/src/providers/memory.ts +142 -0
  187. package/src/runtime/audit-logger.ts +39 -0
  188. package/src/runtime/base.ts +1329 -0
  189. package/src/services/__tests__/access-control.integration.test.ts +443 -0
  190. package/src/services/__tests__/access-control.proof-response-validation.test.ts +578 -0
  191. package/src/services/__tests__/access-control.service.test.ts +970 -0
  192. package/src/services/__tests__/batch-delegation.service.test.ts +351 -0
  193. package/src/services/__tests__/crypto.service.test.ts +531 -0
  194. package/src/services/__tests__/oauth-provider-registry.test.ts +142 -0
  195. package/src/services/__tests__/proof-verifier.integration.test.ts +485 -0
  196. package/src/services/__tests__/proof-verifier.test.ts +489 -0
  197. package/src/services/__tests__/provider-resolution.integration.test.ts +202 -0
  198. package/src/services/__tests__/provider-resolver.test.ts +213 -0
  199. package/src/services/__tests__/storage.service.test.ts +358 -0
  200. package/src/services/access-control.service.ts +990 -0
  201. package/src/services/authorization/authorization-registry.ts +66 -0
  202. package/src/services/authorization/types.ts +71 -0
  203. package/src/services/batch-delegation.service.ts +137 -0
  204. package/src/services/crypto.service.ts +302 -0
  205. package/src/services/errors.ts +76 -0
  206. package/src/services/index.ts +18 -0
  207. package/src/services/oauth-config.service.d.ts +53 -0
  208. package/src/services/oauth-config.service.d.ts.map +1 -0
  209. package/src/services/oauth-config.service.js.map +1 -0
  210. package/src/services/oauth-config.service.ts +192 -0
  211. package/src/services/oauth-provider-registry.d.ts +57 -0
  212. package/src/services/oauth-provider-registry.d.ts.map +1 -0
  213. package/src/services/oauth-provider-registry.js.map +1 -0
  214. package/src/services/oauth-provider-registry.ts +141 -0
  215. package/src/services/oauth-service.ts +544 -0
  216. package/src/services/oauth-token-retrieval.service.ts +245 -0
  217. package/src/services/proof-verifier.ts +478 -0
  218. package/src/services/provider-resolver.d.ts +48 -0
  219. package/src/services/provider-resolver.d.ts.map +1 -0
  220. package/src/services/provider-resolver.js.map +1 -0
  221. package/src/services/provider-resolver.ts +146 -0
  222. package/src/services/provider-validator.ts +170 -0
  223. package/src/services/session-registration.service.ts +317 -0
  224. package/src/services/storage.service.ts +566 -0
  225. package/src/services/tool-context-builder.ts +172 -0
  226. package/src/services/tool-protection.service.ts +982 -0
  227. package/src/types/oauth-required-error.ts +63 -0
  228. package/src/types/tool-protection.ts +155 -0
  229. package/src/utils/__tests__/did-helpers.test.ts +101 -0
  230. package/src/utils/base64.ts +148 -0
  231. package/src/utils/cors.ts +83 -0
  232. package/src/utils/did-helpers.ts +150 -0
  233. package/src/utils/index.ts +8 -0
  234. package/src/utils/storage-keys.ts +278 -0
  235. package/tsconfig.json +21 -0
  236. package/vitest.config.ts +56 -0
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Delegation Graph Manager
3
+ *
4
+ * Tracks parent-child relationships between delegation credentials.
5
+ * Critical for cascading revocation per Delegation-Revocation.md.
6
+ *
7
+ * SOLID Principles:
8
+ * - Single Responsibility: Only manages delegation relationships
9
+ * - Open/Closed: Extensible via storage provider interface
10
+ * - Liskov Substitution: Any storage provider can be used
11
+ * - Interface Segregation: Minimal graph operations interface
12
+ * - Dependency Inversion: Depends on storage abstraction
13
+ *
14
+ * Related Spec: MCP-I §4.4, Delegation Chains
15
+ * Python Reference: Delegation-Revocation.md:45-67
16
+ */
17
+
18
+ /**
19
+ * Delegation node in the graph
20
+ */
21
+ export interface DelegationNode {
22
+ /** Delegation credential ID */
23
+ id: string;
24
+
25
+ /** Parent delegation ID (null for root) */
26
+ parentId: string | null;
27
+
28
+ /** Child delegation IDs */
29
+ children: string[];
30
+
31
+ /** Issuer DID */
32
+ issuerDid: string;
33
+
34
+ /** Subject DID */
35
+ subjectDid: string;
36
+
37
+ /** Credential status reference (for revocation) */
38
+ credentialStatusId?: string;
39
+ }
40
+
41
+ /**
42
+ * Storage provider interface for delegation graphs
43
+ *
44
+ * Platform-specific implementations (CloudflareKV, DynamoDB, etc.)
45
+ */
46
+ export interface DelegationGraphStorageProvider {
47
+ /**
48
+ * Get a delegation node by ID
49
+ */
50
+ getNode(delegationId: string): Promise<DelegationNode | null>;
51
+
52
+ /**
53
+ * Save a delegation node
54
+ */
55
+ setNode(node: DelegationNode): Promise<void>;
56
+
57
+ /**
58
+ * Get all children of a delegation
59
+ */
60
+ getChildren(delegationId: string): Promise<DelegationNode[]>;
61
+
62
+ /**
63
+ * Get the full chain from root to this delegation
64
+ */
65
+ getChain(delegationId: string): Promise<DelegationNode[]>;
66
+
67
+ /**
68
+ * Get all descendants (children, grandchildren, etc.)
69
+ */
70
+ getDescendants(delegationId: string): Promise<DelegationNode[]>;
71
+
72
+ /**
73
+ * Delete a node (used for cleanup)
74
+ */
75
+ deleteNode(delegationId: string): Promise<void>;
76
+ }
77
+
78
+ /**
79
+ * Delegation Graph Manager
80
+ *
81
+ * Manages the tree/graph structure of delegations.
82
+ * Per Delegation-Revocation.md:
83
+ * - Track parent-child relationships
84
+ * - Support chain validation
85
+ * - Enable cascading revocation
86
+ */
87
+ export class DelegationGraphManager {
88
+ constructor(private storage: DelegationGraphStorageProvider) {}
89
+
90
+ /**
91
+ * Register a new delegation in the graph
92
+ *
93
+ * @param delegation - The delegation to register
94
+ * @returns The created node
95
+ */
96
+ async registerDelegation(params: {
97
+ id: string;
98
+ parentId: string | null;
99
+ issuerDid: string;
100
+ subjectDid: string;
101
+ credentialStatusId?: string;
102
+ }): Promise<DelegationNode> {
103
+ const node: DelegationNode = {
104
+ id: params.id,
105
+ parentId: params.parentId,
106
+ children: [],
107
+ issuerDid: params.issuerDid,
108
+ subjectDid: params.subjectDid,
109
+ credentialStatusId: params.credentialStatusId,
110
+ };
111
+
112
+ // Save the node
113
+ await this.storage.setNode(node);
114
+
115
+ // If has parent, add this as a child to parent
116
+ if (params.parentId) {
117
+ await this.addChildToParent(params.parentId, params.id);
118
+ }
119
+
120
+ return node;
121
+ }
122
+
123
+ /**
124
+ * Add a child to a parent node
125
+ *
126
+ * @param parentId - Parent delegation ID
127
+ * @param childId - Child delegation ID
128
+ */
129
+ private async addChildToParent(
130
+ parentId: string,
131
+ childId: string
132
+ ): Promise<void> {
133
+ const parent = await this.storage.getNode(parentId);
134
+ if (!parent) {
135
+ throw new Error(`Parent delegation not found: ${parentId}`);
136
+ }
137
+
138
+ // Add child if not already present
139
+ if (!parent.children.includes(childId)) {
140
+ parent.children.push(childId);
141
+ await this.storage.setNode(parent);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Get a delegation node
147
+ *
148
+ * @param delegationId - The delegation ID
149
+ * @returns The node, or null if not found
150
+ */
151
+ async getNode(delegationId: string): Promise<DelegationNode | null> {
152
+ return this.storage.getNode(delegationId);
153
+ }
154
+
155
+ /**
156
+ * Get all direct children of a delegation
157
+ *
158
+ * @param delegationId - The parent delegation ID
159
+ * @returns Array of child nodes
160
+ */
161
+ async getChildren(delegationId: string): Promise<DelegationNode[]> {
162
+ return this.storage.getChildren(delegationId);
163
+ }
164
+
165
+ /**
166
+ * Get all descendants (children, grandchildren, etc.)
167
+ *
168
+ * Used for cascading revocation.
169
+ * Per Delegation-Revocation.md:56-67
170
+ *
171
+ * @param delegationId - The parent delegation ID
172
+ * @returns Array of all descendant nodes
173
+ */
174
+ async getDescendants(delegationId: string): Promise<DelegationNode[]> {
175
+ return this.storage.getDescendants(delegationId);
176
+ }
177
+
178
+ /**
179
+ * Get the full delegation chain from root to this node
180
+ *
181
+ * Used for chain validation.
182
+ *
183
+ * @param delegationId - The delegation ID
184
+ * @returns Array of nodes from root to this node
185
+ */
186
+ async getChain(delegationId: string): Promise<DelegationNode[]> {
187
+ return this.storage.getChain(delegationId);
188
+ }
189
+
190
+ /**
191
+ * Check if delegation A is an ancestor of delegation B
192
+ *
193
+ * @param ancestorId - Potential ancestor ID
194
+ * @param descendantId - Potential descendant ID
195
+ * @returns true if ancestorId is an ancestor of descendantId
196
+ */
197
+ async isAncestor(
198
+ ancestorId: string,
199
+ descendantId: string
200
+ ): Promise<boolean> {
201
+ const chain = await this.getChain(descendantId);
202
+ return chain.some((node) => node.id === ancestorId);
203
+ }
204
+
205
+ /**
206
+ * Get the depth of a delegation in the tree
207
+ *
208
+ * @param delegationId - The delegation ID
209
+ * @returns Depth (0 for root, 1 for immediate child, etc.)
210
+ */
211
+ async getDepth(delegationId: string): Promise<number> {
212
+ const chain = await this.getChain(delegationId);
213
+ return chain.length - 1; // -1 because chain includes the node itself
214
+ }
215
+
216
+ /**
217
+ * Validate that a delegation chain is properly formed
218
+ *
219
+ * Checks that:
220
+ * - Each child's issuer is the parent's subject
221
+ * - No cycles exist
222
+ * - Chain is continuous
223
+ *
224
+ * @param delegationId - The delegation ID to validate
225
+ * @returns Validation result
226
+ */
227
+ async validateChain(delegationId: string): Promise<{
228
+ valid: boolean;
229
+ reason?: string;
230
+ }> {
231
+ const chain = await this.getChain(delegationId);
232
+
233
+ if (chain.length === 0) {
234
+ return { valid: false, reason: 'Delegation not found' };
235
+ }
236
+
237
+ // Check each link in the chain
238
+ for (let i = 1; i < chain.length; i++) {
239
+ const parent = chain[i - 1];
240
+ const child = chain[i];
241
+
242
+ // Child's issuer must be parent's subject
243
+ if (child.issuerDid !== parent.subjectDid) {
244
+ return {
245
+ valid: false,
246
+ reason: `Invalid chain: ${child.id} issued by ${child.issuerDid} but parent ${parent.id} subject is ${parent.subjectDid}`,
247
+ };
248
+ }
249
+
250
+ // Child's parent pointer must match parent's ID
251
+ if (child.parentId !== parent.id) {
252
+ return {
253
+ valid: false,
254
+ reason: `Invalid chain: ${child.id} parentId=${child.parentId} but actual parent is ${parent.id}`,
255
+ };
256
+ }
257
+ }
258
+
259
+ return { valid: true };
260
+ }
261
+
262
+ /**
263
+ * Remove a delegation from the graph
264
+ *
265
+ * Note: This doesn't cascade - use CascadingRevocationManager for that.
266
+ *
267
+ * @param delegationId - The delegation ID to remove
268
+ */
269
+ async removeDelegation(delegationId: string): Promise<void> {
270
+ const node = await this.storage.getNode(delegationId);
271
+ if (!node) return;
272
+
273
+ // Remove from parent's children list
274
+ if (node.parentId) {
275
+ const parent = await this.storage.getNode(node.parentId);
276
+ if (parent) {
277
+ parent.children = parent.children.filter((id) => id !== delegationId);
278
+ await this.storage.setNode(parent);
279
+ }
280
+ }
281
+
282
+ // Delete the node
283
+ await this.storage.deleteNode(delegationId);
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Create a delegation graph manager
289
+ *
290
+ * Convenience factory function.
291
+ *
292
+ * @param storage - Storage provider
293
+ * @returns DelegationGraphManager instance
294
+ */
295
+ export function createDelegationGraph(
296
+ storage: DelegationGraphStorageProvider
297
+ ): DelegationGraphManager {
298
+ return new DelegationGraphManager(storage);
299
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Delegation Module Exports (Platform-Agnostic)
3
+ *
4
+ * W3C VC-based delegation issuance and verification.
5
+ * Platform-specific adapters (Node.js, Cloudflare) provide signing/verification functions.
6
+ */
7
+
8
+ export * from './vc-issuer';
9
+ export * from './vc-verifier';
10
+ export * from './bitstring';
11
+ export * from './statuslist-manager';
12
+ export * from './delegation-graph';
13
+ export * from './cascading-revocation';
14
+ export * from './utils';
@@ -0,0 +1,353 @@
1
+ /**
2
+ * StatusList2021 Manager
3
+ *
4
+ * Manages StatusList2021 credentials for efficient delegation revocation.
5
+ * Follows the Python POC design from Delegation-Revocation.md.
6
+ *
7
+ * SOLID Principles:
8
+ * - Single Responsibility: Manages status list allocation and updates
9
+ * - Open/Closed: Extensible via storage provider interface
10
+ * - Liskov Substitution: Any storage provider can be used
11
+ * - Interface Segregation: Minimal storage interface
12
+ * - Dependency Inversion: Depends on abstractions (storage, signing)
13
+ *
14
+ * Related Spec: W3C StatusList2021
15
+ * Python Reference: Delegation-Revocation.md
16
+ */
17
+
18
+ import type {
19
+ StatusList2021Credential,
20
+ CredentialStatus,
21
+ } from '@kya-os/contracts';
22
+ import { BitstringManager, CompressionFunction, DecompressionFunction } from './bitstring';
23
+ import { VCSigningFunction } from './vc-issuer';
24
+ import { canonicalizeJSON } from './utils';
25
+
26
+ /**
27
+ * Storage provider interface for status lists
28
+ *
29
+ * Platform-specific implementations (CloudflareKV, DynamoDB, Redis, etc.)
30
+ * implement this interface.
31
+ */
32
+ export interface StatusListStorageProvider {
33
+ /**
34
+ * Get a status list credential by ID
35
+ *
36
+ * @param statusListId - The status list URL
37
+ * @returns The status list credential, or null if not found
38
+ */
39
+ getStatusList(statusListId: string): Promise<StatusList2021Credential | null>;
40
+
41
+ /**
42
+ * Save a status list credential
43
+ *
44
+ * @param statusListId - The status list URL
45
+ * @param credential - The status list credential
46
+ */
47
+ setStatusList(
48
+ statusListId: string,
49
+ credential: StatusList2021Credential
50
+ ): Promise<void>;
51
+
52
+ /**
53
+ * Allocate a new index in a status list
54
+ *
55
+ * Thread-safe allocation of the next available index.
56
+ *
57
+ * @param statusListId - The status list URL
58
+ * @returns The allocated index
59
+ */
60
+ allocateIndex(statusListId: string): Promise<number>;
61
+ }
62
+
63
+ /**
64
+ * Identity provider for signing status list credentials
65
+ */
66
+ export interface StatusListIdentityProvider {
67
+ /** Get the DID of this identity */
68
+ getDid(): string;
69
+
70
+ /** Get the key ID of this identity */
71
+ getKeyId(): string;
72
+ }
73
+
74
+ /**
75
+ * StatusList2021 Manager
76
+ *
77
+ * Manages status lists for efficient delegation revocation.
78
+ * Per Delegation-Revocation.md:
79
+ * - StatusList2021 for efficient revocation distribution
80
+ * - Compressed bitstrings for scalability
81
+ * - Separate lists for revocation vs suspension
82
+ */
83
+ export class StatusList2021Manager {
84
+ private statusListBaseUrl: string;
85
+ private defaultListSize: number;
86
+
87
+ constructor(
88
+ private storage: StatusListStorageProvider,
89
+ private identity: StatusListIdentityProvider,
90
+ private signingFunction: VCSigningFunction,
91
+ private compressor: CompressionFunction,
92
+ private decompressor: DecompressionFunction,
93
+ options?: {
94
+ /** Base URL for status lists (e.g., "https://example.com/status") */
95
+ statusListBaseUrl?: string;
96
+ /** Default size for new status lists (number of entries) */
97
+ defaultListSize?: number;
98
+ }
99
+ ) {
100
+ this.statusListBaseUrl = options?.statusListBaseUrl || 'https://status.example.com';
101
+ this.defaultListSize = options?.defaultListSize || 131072; // 128K entries (16KB compressed)
102
+ }
103
+
104
+ /**
105
+ * Allocate a status entry for a new delegation credential
106
+ *
107
+ * Per Delegation-Revocation.md: Each delegation gets a unique status list entry.
108
+ *
109
+ * @param purpose - "revocation" or "suspension"
110
+ * @returns CredentialStatus entry for the delegation VC
111
+ */
112
+ async allocateStatusEntry(
113
+ purpose: 'revocation' | 'suspension'
114
+ ): Promise<CredentialStatus> {
115
+ // Determine which status list to use
116
+ const statusListId = `${this.statusListBaseUrl}/${purpose}/v1`;
117
+
118
+ // Allocate index in the status list (thread-safe)
119
+ const index = await this.storage.allocateIndex(statusListId);
120
+
121
+ // Ensure the status list exists
122
+ await this.ensureStatusListExists(statusListId, purpose);
123
+
124
+ // Create the credential status entry
125
+ const credentialStatus: CredentialStatus = {
126
+ id: `${statusListId}#${index}`,
127
+ type: 'StatusList2021Entry',
128
+ statusPurpose: purpose,
129
+ statusListIndex: index.toString(),
130
+ statusListCredential: statusListId,
131
+ };
132
+
133
+ return credentialStatus;
134
+ }
135
+
136
+ /**
137
+ * Revoke or suspend a delegation by updating its status
138
+ *
139
+ * @param credentialStatus - The credential status entry from the VC
140
+ * @param revoked - true to revoke/suspend, false to restore
141
+ */
142
+ async updateStatus(
143
+ credentialStatus: CredentialStatus,
144
+ revoked: boolean
145
+ ): Promise<void> {
146
+ const { statusListCredential, statusListIndex } = credentialStatus;
147
+
148
+ // Get the current status list
149
+ const statusList = await this.storage.getStatusList(statusListCredential);
150
+ if (!statusList) {
151
+ throw new Error(`Status list not found: ${statusListCredential}`);
152
+ }
153
+
154
+ // Decode the bitstring
155
+ const manager = await BitstringManager.decode(
156
+ statusList.credentialSubject.encodedList,
157
+ this.compressor,
158
+ this.decompressor
159
+ );
160
+
161
+ // Update the bit
162
+ const index = parseInt(statusListIndex, 10);
163
+ manager.setBit(index, revoked);
164
+
165
+ // Re-encode
166
+ const encodedList = await manager.encode();
167
+
168
+ // Update the credential
169
+ const updatedCredential: StatusList2021Credential = {
170
+ ...statusList,
171
+ credentialSubject: {
172
+ ...statusList.credentialSubject,
173
+ encodedList,
174
+ },
175
+ };
176
+
177
+ // Re-sign the credential (proof changes when content changes)
178
+ const unsignedCredential = { ...updatedCredential };
179
+ delete (unsignedCredential as any).proof;
180
+
181
+ const canonicalVC = canonicalizeJSON(unsignedCredential);
182
+ const proof = await this.signingFunction(
183
+ canonicalVC,
184
+ this.identity.getDid(),
185
+ this.identity.getKeyId()
186
+ );
187
+
188
+ const signedCredential: StatusList2021Credential = {
189
+ ...updatedCredential,
190
+ proof,
191
+ };
192
+
193
+ // Save the updated status list
194
+ await this.storage.setStatusList(statusListCredential, signedCredential);
195
+ }
196
+
197
+ /**
198
+ * Check if a credential is revoked
199
+ *
200
+ * @param credentialStatus - The credential status entry
201
+ * @returns true if revoked/suspended, false otherwise
202
+ */
203
+ async checkStatus(credentialStatus: CredentialStatus): Promise<boolean> {
204
+ const { statusListCredential, statusListIndex } = credentialStatus;
205
+
206
+ // Get the status list
207
+ const statusList = await this.storage.getStatusList(statusListCredential);
208
+ if (!statusList) {
209
+ // Status list doesn't exist = not revoked
210
+ return false;
211
+ }
212
+
213
+ // Decode and check the bit
214
+ const manager = await BitstringManager.decode(
215
+ statusList.credentialSubject.encodedList,
216
+ this.compressor,
217
+ this.decompressor
218
+ );
219
+
220
+ const index = parseInt(statusListIndex, 10);
221
+ return manager.getBit(index);
222
+ }
223
+
224
+ /**
225
+ * Get all revoked indices in a status list
226
+ *
227
+ * Useful for debugging or auditing.
228
+ *
229
+ * @param statusListId - The status list URL
230
+ * @returns Array of revoked indices
231
+ */
232
+ async getRevokedIndices(statusListId: string): Promise<number[]> {
233
+ const statusList = await this.storage.getStatusList(statusListId);
234
+ if (!statusList) {
235
+ return [];
236
+ }
237
+
238
+ const manager = await BitstringManager.decode(
239
+ statusList.credentialSubject.encodedList,
240
+ this.compressor,
241
+ this.decompressor
242
+ );
243
+
244
+ return manager.getSetBits();
245
+ }
246
+
247
+ /**
248
+ * Ensure a status list exists, creating it if needed
249
+ *
250
+ * @param statusListId - The status list URL
251
+ * @param purpose - "revocation" or "suspension"
252
+ */
253
+ private async ensureStatusListExists(
254
+ statusListId: string,
255
+ purpose: 'revocation' | 'suspension'
256
+ ): Promise<void> {
257
+ // Check if it already exists
258
+ const existing = await this.storage.getStatusList(statusListId);
259
+ if (existing) {
260
+ return;
261
+ }
262
+
263
+ // Create a new status list
264
+ const manager = new BitstringManager(
265
+ this.defaultListSize,
266
+ this.compressor,
267
+ this.decompressor
268
+ );
269
+ const encodedList = await manager.encode();
270
+
271
+ // Create the unsigned credential
272
+ const unsignedCredential = {
273
+ '@context': [
274
+ 'https://www.w3.org/2018/credentials/v1',
275
+ 'https://w3id.org/vc/status-list/2021/v1',
276
+ ] as [string, string],
277
+ id: statusListId,
278
+ type: ['VerifiableCredential', 'StatusList2021Credential'] as ['VerifiableCredential', 'StatusList2021Credential'],
279
+ issuer: this.identity.getDid(),
280
+ issuanceDate: new Date().toISOString(),
281
+ credentialSubject: {
282
+ id: `${statusListId}#list`,
283
+ type: 'StatusList2021' as const,
284
+ statusPurpose: purpose,
285
+ encodedList,
286
+ },
287
+ };
288
+
289
+ // Sign it
290
+ const canonicalVC = canonicalizeJSON(unsignedCredential);
291
+ const proof = await this.signingFunction(
292
+ canonicalVC,
293
+ this.identity.getDid(),
294
+ this.identity.getKeyId()
295
+ );
296
+
297
+ const signedCredential: StatusList2021Credential = {
298
+ ...unsignedCredential,
299
+ proof,
300
+ };
301
+
302
+ // Store it
303
+ await this.storage.setStatusList(statusListId, signedCredential);
304
+ }
305
+
306
+ /**
307
+ * Get the status list base URL
308
+ */
309
+ getStatusListBaseUrl(): string {
310
+ return this.statusListBaseUrl;
311
+ }
312
+
313
+ /**
314
+ * Get the default list size
315
+ */
316
+ getDefaultListSize(): number {
317
+ return this.defaultListSize;
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Create a StatusList2021 manager
323
+ *
324
+ * Convenience factory function.
325
+ *
326
+ * @param storage - Storage provider
327
+ * @param identity - Identity provider
328
+ * @param signingFunction - VC signing function
329
+ * @param compressor - Compression function
330
+ * @param decompressor - Decompression function
331
+ * @param options - Manager options
332
+ * @returns StatusList2021Manager instance
333
+ */
334
+ export function createStatusListManager(
335
+ storage: StatusListStorageProvider,
336
+ identity: StatusListIdentityProvider,
337
+ signingFunction: VCSigningFunction,
338
+ compressor: CompressionFunction,
339
+ decompressor: DecompressionFunction,
340
+ options?: {
341
+ statusListBaseUrl?: string;
342
+ defaultListSize?: number;
343
+ }
344
+ ): StatusList2021Manager {
345
+ return new StatusList2021Manager(
346
+ storage,
347
+ identity,
348
+ signingFunction,
349
+ compressor,
350
+ decompressor,
351
+ options
352
+ );
353
+ }