@kya-os/mcp-i-core 1.2.3-canary.7 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +9 -0
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test.log +2979 -0
- package/COMPLIANCE_IMPROVEMENT_REPORT.md +483 -0
- package/Composer 3.md +615 -0
- package/GPT-5.md +1169 -0
- package/OPUS-plan.md +352 -0
- package/PHASE_3_AND_4.1_SUMMARY.md +585 -0
- package/PHASE_3_SUMMARY.md +317 -0
- package/PHASE_4.1.3_SUMMARY.md +428 -0
- package/PHASE_4.1_COMPLETE.md +525 -0
- package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +1240 -0
- package/SCHEMA_COMPLIANCE_REPORT.md +275 -0
- package/TEST_PLAN.md +571 -0
- package/coverage/coverage-final.json +57 -0
- package/dist/__tests__/utils/mock-providers.d.ts +1 -2
- package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
- package/dist/__tests__/utils/mock-providers.js.map +1 -1
- package/dist/cache/oauth-config-cache.d.ts +69 -0
- package/dist/cache/oauth-config-cache.d.ts.map +1 -0
- package/dist/cache/oauth-config-cache.js +76 -0
- package/dist/cache/oauth-config-cache.js.map +1 -0
- package/dist/identity/idp-token-resolver.d.ts +53 -0
- package/dist/identity/idp-token-resolver.d.ts.map +1 -0
- package/dist/identity/idp-token-resolver.js +108 -0
- package/dist/identity/idp-token-resolver.js.map +1 -0
- package/dist/identity/idp-token-storage.interface.d.ts +42 -0
- package/dist/identity/idp-token-storage.interface.d.ts.map +1 -0
- package/dist/identity/idp-token-storage.interface.js +12 -0
- package/dist/identity/idp-token-storage.interface.js.map +1 -0
- package/dist/identity/user-did-manager.d.ts +39 -1
- package/dist/identity/user-did-manager.d.ts.map +1 -1
- package/dist/identity/user-did-manager.js +69 -3
- package/dist/identity/user-did-manager.js.map +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/audit-logger.d.ts +37 -0
- package/dist/runtime/audit-logger.d.ts.map +1 -0
- package/dist/runtime/audit-logger.js +9 -0
- package/dist/runtime/audit-logger.js.map +1 -0
- package/dist/runtime/base.d.ts +58 -2
- package/dist/runtime/base.d.ts.map +1 -1
- package/dist/runtime/base.js +266 -11
- package/dist/runtime/base.js.map +1 -1
- package/dist/services/access-control.service.d.ts.map +1 -1
- package/dist/services/access-control.service.js +200 -35
- package/dist/services/access-control.service.js.map +1 -1
- package/dist/services/authorization/authorization-registry.d.ts +29 -0
- package/dist/services/authorization/authorization-registry.d.ts.map +1 -0
- package/dist/services/authorization/authorization-registry.js +57 -0
- package/dist/services/authorization/authorization-registry.js.map +1 -0
- package/dist/services/authorization/types.d.ts +53 -0
- package/dist/services/authorization/types.d.ts.map +1 -0
- package/dist/services/authorization/types.js +10 -0
- package/dist/services/authorization/types.js.map +1 -0
- package/dist/services/batch-delegation.service.d.ts +53 -0
- package/dist/services/batch-delegation.service.d.ts.map +1 -0
- package/dist/services/batch-delegation.service.js +95 -0
- package/dist/services/batch-delegation.service.js.map +1 -0
- package/dist/services/oauth-config.service.d.ts +53 -0
- package/dist/services/oauth-config.service.d.ts.map +1 -0
- package/dist/services/oauth-config.service.js +119 -0
- package/dist/services/oauth-config.service.js.map +1 -0
- package/dist/services/oauth-provider-registry.d.ts +88 -0
- package/dist/services/oauth-provider-registry.d.ts.map +1 -0
- package/dist/services/oauth-provider-registry.js +128 -0
- package/dist/services/oauth-provider-registry.js.map +1 -0
- package/dist/services/oauth-service.d.ts +77 -0
- package/dist/services/oauth-service.d.ts.map +1 -0
- package/dist/services/oauth-service.js +348 -0
- package/dist/services/oauth-service.js.map +1 -0
- package/dist/services/oauth-token-retrieval.service.d.ts +49 -0
- package/dist/services/oauth-token-retrieval.service.d.ts.map +1 -0
- package/dist/services/oauth-token-retrieval.service.js +150 -0
- package/dist/services/oauth-token-retrieval.service.js.map +1 -0
- package/dist/services/provider-resolver.d.ts +48 -0
- package/dist/services/provider-resolver.d.ts.map +1 -0
- package/dist/services/provider-resolver.js +121 -0
- package/dist/services/provider-resolver.js.map +1 -0
- package/dist/services/provider-validator.d.ts +55 -0
- package/dist/services/provider-validator.d.ts.map +1 -0
- package/dist/services/provider-validator.js +135 -0
- package/dist/services/provider-validator.js.map +1 -0
- package/dist/services/tool-context-builder.d.ts +57 -0
- package/dist/services/tool-context-builder.d.ts.map +1 -0
- package/dist/services/tool-context-builder.js +125 -0
- package/dist/services/tool-context-builder.js.map +1 -0
- package/dist/services/tool-protection.service.d.ts +87 -10
- package/dist/services/tool-protection.service.d.ts.map +1 -1
- package/dist/services/tool-protection.service.js +282 -112
- package/dist/services/tool-protection.service.js.map +1 -1
- package/dist/types/oauth-required-error.d.ts +40 -0
- package/dist/types/oauth-required-error.d.ts.map +1 -0
- package/dist/types/oauth-required-error.js +40 -0
- package/dist/types/oauth-required-error.js.map +1 -0
- package/dist/utils/did-helpers.d.ts +33 -0
- package/dist/utils/did-helpers.d.ts.map +1 -1
- package/dist/utils/did-helpers.js +40 -0
- package/dist/utils/did-helpers.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/docs/API_REFERENCE.md +1362 -0
- package/docs/COMPLIANCE_MATRIX.md +691 -0
- package/docs/STATUSLIST2021_GUIDE.md +696 -0
- package/docs/W3C_VC_DELEGATION_GUIDE.md +710 -0
- package/package.json +24 -50
- package/scripts/audit-compliance.ts +724 -0
- package/src/__tests__/cache/tool-protection-cache.test.ts +640 -0
- package/src/__tests__/config/provider-runtime-config.test.ts +309 -0
- package/src/__tests__/delegation-e2e.test.ts +690 -0
- package/src/__tests__/identity/user-did-manager.test.ts +213 -0
- package/src/__tests__/index.test.ts +56 -0
- package/src/__tests__/integration/full-flow.test.ts +776 -0
- package/src/__tests__/integration.test.ts +281 -0
- package/src/__tests__/providers/base.test.ts +173 -0
- package/src/__tests__/providers/memory.test.ts +319 -0
- package/src/__tests__/regression/phase2-regression.test.ts +429 -0
- package/src/__tests__/runtime/audit-logger.test.ts +154 -0
- package/src/__tests__/runtime/base-extensions.test.ts +593 -0
- package/src/__tests__/runtime/base.test.ts +869 -0
- package/src/__tests__/runtime/delegation-flow.test.ts +164 -0
- package/src/__tests__/runtime/proof-client-did.test.ts +375 -0
- package/src/__tests__/runtime/route-interception.test.ts +686 -0
- package/src/__tests__/runtime/tool-protection-enforcement.test.ts +908 -0
- package/src/__tests__/services/agentshield-integration.test.ts +784 -0
- package/src/__tests__/services/provider-resolver-edge-cases.test.ts +591 -0
- package/src/__tests__/services/tool-protection-oauth-provider.test.ts +480 -0
- package/src/__tests__/services/tool-protection.service.test.ts +1366 -0
- package/src/__tests__/utils/mock-providers.ts +340 -0
- package/src/cache/oauth-config-cache.d.ts +69 -0
- package/src/cache/oauth-config-cache.d.ts.map +1 -0
- package/src/cache/oauth-config-cache.js.map +1 -0
- package/src/cache/oauth-config-cache.ts +123 -0
- package/src/cache/tool-protection-cache.ts +171 -0
- package/src/compliance/EXAMPLE.md +412 -0
- package/src/compliance/__tests__/schema-verifier.test.ts +797 -0
- package/src/compliance/index.ts +8 -0
- package/src/compliance/schema-registry.ts +460 -0
- package/src/compliance/schema-verifier.ts +708 -0
- package/src/config/__tests__/remote-config.spec.ts +268 -0
- package/src/config/remote-config.ts +174 -0
- package/src/config.ts +309 -0
- package/src/delegation/__tests__/audience-validator.test.ts +112 -0
- package/src/delegation/__tests__/bitstring.test.ts +346 -0
- package/src/delegation/__tests__/cascading-revocation.test.ts +628 -0
- package/src/delegation/__tests__/delegation-graph.test.ts +584 -0
- package/src/delegation/__tests__/utils.test.ts +152 -0
- package/src/delegation/__tests__/vc-issuer.test.ts +442 -0
- package/src/delegation/__tests__/vc-verifier.test.ts +922 -0
- package/src/delegation/audience-validator.ts +52 -0
- package/src/delegation/bitstring.ts +278 -0
- package/src/delegation/cascading-revocation.ts +370 -0
- package/src/delegation/delegation-graph.ts +299 -0
- package/src/delegation/index.ts +14 -0
- package/src/delegation/statuslist-manager.ts +353 -0
- package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
- package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
- package/src/delegation/storage/index.ts +9 -0
- package/src/delegation/storage/memory-graph-storage.ts +178 -0
- package/src/delegation/storage/memory-statuslist-storage.ts +77 -0
- package/src/delegation/utils.ts +42 -0
- package/src/delegation/vc-issuer.ts +232 -0
- package/src/delegation/vc-verifier.ts +568 -0
- package/src/identity/idp-token-resolver.ts +147 -0
- package/src/identity/idp-token-storage.interface.ts +59 -0
- package/src/identity/user-did-manager.ts +370 -0
- package/src/index.ts +260 -0
- package/src/providers/base.d.ts +91 -0
- package/src/providers/base.d.ts.map +1 -0
- package/src/providers/base.js.map +1 -0
- package/src/providers/base.ts +96 -0
- package/src/providers/memory.ts +142 -0
- package/src/runtime/audit-logger.ts +39 -0
- package/src/runtime/base.ts +1329 -0
- package/src/services/__tests__/access-control.integration.test.ts +443 -0
- package/src/services/__tests__/access-control.proof-response-validation.test.ts +578 -0
- package/src/services/__tests__/access-control.service.test.ts +970 -0
- package/src/services/__tests__/batch-delegation.service.test.ts +351 -0
- package/src/services/__tests__/crypto.service.test.ts +531 -0
- package/src/services/__tests__/oauth-provider-registry.test.ts +142 -0
- package/src/services/__tests__/proof-verifier.integration.test.ts +485 -0
- package/src/services/__tests__/proof-verifier.test.ts +489 -0
- package/src/services/__tests__/provider-resolution.integration.test.ts +202 -0
- package/src/services/__tests__/provider-resolver.test.ts +213 -0
- package/src/services/__tests__/storage.service.test.ts +358 -0
- package/src/services/access-control.service.ts +990 -0
- package/src/services/authorization/authorization-registry.ts +66 -0
- package/src/services/authorization/types.ts +71 -0
- package/src/services/batch-delegation.service.ts +137 -0
- package/src/services/crypto.service.ts +302 -0
- package/src/services/errors.ts +76 -0
- package/src/services/index.ts +9 -0
- package/src/services/oauth-config.service.d.ts +53 -0
- package/src/services/oauth-config.service.d.ts.map +1 -0
- package/src/services/oauth-config.service.js.map +1 -0
- package/src/services/oauth-config.service.ts +169 -0
- package/src/services/oauth-provider-registry.d.ts +57 -0
- package/src/services/oauth-provider-registry.d.ts.map +1 -0
- package/src/services/oauth-provider-registry.js.map +1 -0
- package/src/services/oauth-provider-registry.ts +141 -0
- package/src/services/oauth-service.ts +510 -0
- package/src/services/oauth-token-retrieval.service.ts +245 -0
- package/src/services/proof-verifier.ts +478 -0
- package/src/services/provider-resolver.d.ts +48 -0
- package/src/services/provider-resolver.d.ts.map +1 -0
- package/src/services/provider-resolver.js.map +1 -0
- package/src/services/provider-resolver.ts +146 -0
- package/src/services/provider-validator.ts +170 -0
- package/src/services/storage.service.ts +566 -0
- package/src/services/tool-context-builder.ts +172 -0
- package/src/services/tool-protection.service.ts +958 -0
- package/src/types/oauth-required-error.ts +63 -0
- package/src/types/tool-protection.ts +155 -0
- package/src/utils/__tests__/did-helpers.test.ts +101 -0
- package/src/utils/base64.ts +148 -0
- package/src/utils/cors.ts +83 -0
- package/src/utils/did-helpers.ts +150 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/storage-keys.ts +278 -0
- package/tsconfig.json +21 -0
- 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
|
+
}
|