@private.me/xbind 1.3.5 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES.md +212 -0
- package/README.md +388 -6
- package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1 -1920
- package/dist-standalone/_deps/shared/cjs/errors.js +1 -639
- package/dist-standalone/_deps/shared/cjs/index.js +1 -496
- package/dist-standalone/_deps/shared/cjs/types.js +1 -317
- package/dist-standalone/_deps/shared/errors.js +1 -255
- package/dist-standalone/_deps/shared/index.js +1 -74
- package/dist-standalone/_deps/shared/types.js +1 -90
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
- package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/search.js +1 -1
- package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
- package/dist-standalone/_deps/xchange/errors.js +1 -1
- package/dist-standalone/_deps/xchange/index.js +1 -1
- package/dist-standalone/_deps/xchange/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/xchange.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
- package/dist-standalone/_deps/xregistry/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/index.js +1 -1
- package/dist-standalone/_deps/xregistry/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/types.js +1 -1
- package/dist-standalone/agent-call.js +1 -642
- package/dist-standalone/agent-sdk.js +1 -328
- package/dist-standalone/agent.d.ts +95 -5
- package/dist-standalone/agent.js +1 -1545
- package/dist-standalone/approval.js +1 -193
- package/dist-standalone/async-iterators.d.ts +275 -0
- package/dist-standalone/async-iterators.js +1 -0
- package/dist-standalone/auth.js +1 -219
- package/dist-standalone/auto-accept.js +1 -229
- package/dist-standalone/backup-config.js +1 -201
- package/dist-standalone/backup.d.ts +114 -0
- package/dist-standalone/backup.js +1 -0
- package/dist-standalone/batch-operations.d.ts +297 -0
- package/dist-standalone/batch-operations.js +1 -0
- package/dist-standalone/cancellation.d.ts +301 -0
- package/dist-standalone/cancellation.js +1 -0
- package/dist-standalone/checkpoint.js +1 -186
- package/dist-standalone/circuit-breaker.d.ts +351 -0
- package/dist-standalone/circuit-breaker.js +1 -0
- package/dist-standalone/cjs/agent-call.js +1 -651
- package/dist-standalone/cjs/agent-sdk.js +1 -332
- package/dist-standalone/cjs/agent.js +1 -1582
- package/dist-standalone/cjs/approval.js +1 -199
- package/dist-standalone/cjs/async-iterators.js +1 -0
- package/dist-standalone/cjs/auth.js +1 -225
- package/dist-standalone/cjs/auto-accept.js +1 -233
- package/dist-standalone/cjs/backup-config.js +1 -207
- package/dist-standalone/cjs/backup.js +1 -0
- package/dist-standalone/cjs/batch-operations.js +1 -0
- package/dist-standalone/cjs/cancellation.js +1 -0
- package/dist-standalone/cjs/checkpoint.js +1 -193
- package/dist-standalone/cjs/circuit-breaker.js +1 -0
- package/dist-standalone/cjs/cli/init.js +1 -486
- package/dist-standalone/cjs/config-validation.js +1 -0
- package/dist-standalone/cjs/connect.js +1 -312
- package/dist-standalone/cjs/connection-pool.js +1 -0
- package/dist-standalone/cjs/correlation-id.js +1 -339
- package/dist-standalone/cjs/crypto-utils.js +1 -0
- package/dist-standalone/cjs/debug-mode.js +1 -0
- package/dist-standalone/cjs/did-document.js +1 -101
- package/dist-standalone/cjs/did-privateme.js +1 -130
- package/dist-standalone/cjs/did-web.js +1 -201
- package/dist-standalone/cjs/discovery.js +1 -462
- package/dist-standalone/cjs/dual-mode.js +1 -251
- package/dist-standalone/cjs/email-templates.js +1 -313
- package/dist-standalone/cjs/email-transport.js +1 -239
- package/dist-standalone/cjs/envelope.js +1 -510
- package/dist-standalone/cjs/errors.js +1 -826
- package/dist-standalone/cjs/event-emitter.js +1 -0
- package/dist-standalone/cjs/gateway-state.js +1 -55
- package/dist-standalone/cjs/gateway-transport.js +1 -120
- package/dist-standalone/cjs/graceful-degradation.js +1 -0
- package/dist-standalone/cjs/guardrails.js +1 -223
- package/dist-standalone/cjs/health-check.js +1 -0
- package/dist-standalone/cjs/http-compat.js +1 -272
- package/dist-standalone/cjs/http-status-map.js +1 -571
- package/dist-standalone/cjs/identity.js +1 -540
- package/dist-standalone/cjs/index.js +1 -237
- package/dist-standalone/cjs/invitation.js +1 -421
- package/dist-standalone/cjs/invite.js +1 -328
- package/dist-standalone/cjs/key-agreement.js +1 -246
- package/dist-standalone/cjs/lazy-init.js +1 -300
- package/dist-standalone/cjs/logger.js +1 -0
- package/dist-standalone/cjs/mdns-discovery.js +1 -202
- package/dist-standalone/cjs/nonce-store.js +1 -66
- package/dist-standalone/cjs/pairing-manager.js +1 -223
- package/dist-standalone/cjs/plugin-system.js +1 -0
- package/dist-standalone/cjs/plugins/logging.js +1 -0
- package/dist-standalone/cjs/plugins/metrics.js +1 -0
- package/dist-standalone/cjs/plugins/validation.js +1 -0
- package/dist-standalone/cjs/policy.js +1 -320
- package/dist-standalone/cjs/progress-callbacks.js +1 -0
- package/dist-standalone/cjs/redis-nonce-store.js +1 -76
- package/dist-standalone/cjs/registry-middleware.js +1 -50
- package/dist-standalone/cjs/retry-strategies.js +1 -0
- package/dist-standalone/cjs/retry-transport.js +1 -102
- package/dist-standalone/cjs/runtime/browser.js +1 -0
- package/dist-standalone/cjs/runtime/edge.js +1 -0
- package/dist-standalone/cjs/runtime/react-native.js +1 -0
- package/dist-standalone/cjs/security-policy.js +1 -245
- package/dist-standalone/cjs/serialization.js +1 -0
- package/dist-standalone/cjs/split-channel.js +1 -177
- package/dist-standalone/cjs/subscription-proof.js +1 -230
- package/dist-standalone/cjs/succession.js +1 -148
- package/dist-standalone/cjs/timeouts.js +1 -0
- package/dist-standalone/cjs/trace-context.js +1 -0
- package/dist-standalone/cjs/trace-spans.js +1 -0
- package/dist-standalone/cjs/transport.js +1 -63
- package/dist-standalone/cjs/trust-registry.js +1 -742
- package/dist-standalone/cjs/types/error-response.js +1 -56
- package/dist-standalone/cjs/vault-auth.js +1 -0
- package/dist-standalone/cjs/vault-store-loader.js +1 -0
- package/dist-standalone/cjs/verify.js +1 -25
- package/dist-standalone/cjs/version-info.js +1 -0
- package/dist-standalone/cjs/xfetch.js +1 -252
- package/dist-standalone/cli/init.js +1 -449
- package/dist-standalone/cli/setup.js +1 -514
- package/dist-standalone/cli/types.js +1 -27
- package/dist-standalone/cli/xbind.js +1 -148
- package/dist-standalone/config-validation.d.ts +185 -0
- package/dist-standalone/config-validation.js +1 -0
- package/dist-standalone/connect.js +1 -274
- package/dist-standalone/connection-pool.d.ts +251 -0
- package/dist-standalone/connection-pool.js +1 -0
- package/dist-standalone/correlation-id.js +1 -326
- package/dist-standalone/crypto-utils.d.ts +60 -0
- package/dist-standalone/crypto-utils.js +1 -0
- package/dist-standalone/debug-mode.d.ts +286 -0
- package/dist-standalone/debug-mode.js +1 -0
- package/dist-standalone/did-document.js +1 -96
- package/dist-standalone/did-privateme.js +1 -121
- package/dist-standalone/did-web.js +1 -196
- package/dist-standalone/discovery.js +1 -458
- package/dist-standalone/dual-mode.js +1 -247
- package/dist-standalone/email-templates.js +1 -309
- package/dist-standalone/email-transport.js +1 -232
- package/dist-standalone/envelope.d.ts +29 -1
- package/dist-standalone/envelope.js +1 -497
- package/dist-standalone/errors.d.ts +10 -0
- package/dist-standalone/errors.js +1 -811
- package/dist-standalone/event-emitter.d.ts +395 -0
- package/dist-standalone/event-emitter.js +1 -0
- package/dist-standalone/gateway-state.js +1 -51
- package/dist-standalone/gateway-transport.js +1 -116
- package/dist-standalone/graceful-degradation.d.ts +246 -0
- package/dist-standalone/graceful-degradation.js +1 -0
- package/dist-standalone/guardrails.js +1 -216
- package/dist-standalone/health-check.d.ts +150 -0
- package/dist-standalone/health-check.js +1 -0
- package/dist-standalone/http-compat.js +1 -267
- package/dist-standalone/http-status-map.js +1 -561
- package/dist-standalone/identity.d.ts +64 -1
- package/dist-standalone/identity.js +1 -515
- package/dist-standalone/index.d.ts +45 -3
- package/dist-standalone/index.js +1 -52
- package/dist-standalone/invitation.js +1 -415
- package/dist-standalone/invite.js +1 -324
- package/dist-standalone/key-agreement.d.ts +61 -13
- package/dist-standalone/key-agreement.js +1 -236
- package/dist-standalone/lazy-init.js +1 -295
- package/dist-standalone/logger.d.ts +77 -0
- package/dist-standalone/logger.js +1 -0
- package/dist-standalone/mdns-discovery.js +1 -195
- package/dist-standalone/nonce-store.d.ts +16 -3
- package/dist-standalone/nonce-store.js +1 -62
- package/dist-standalone/package.json +0 -1
- package/dist-standalone/pairing-manager.js +1 -219
- package/dist-standalone/plugin-system.d.ts +145 -0
- package/dist-standalone/plugin-system.js +1 -0
- package/dist-standalone/policy.js +1 -315
- package/dist-standalone/progress-callbacks.d.ts +394 -0
- package/dist-standalone/progress-callbacks.js +1 -0
- package/dist-standalone/redis-nonce-store.js +1 -72
- package/dist-standalone/registry-middleware.js +1 -47
- package/dist-standalone/retry-strategies.d.ts +382 -0
- package/dist-standalone/retry-strategies.js +1 -0
- package/dist-standalone/retry-transport.js +1 -98
- package/dist-standalone/security-policy.js +1 -239
- package/dist-standalone/serialization.d.ts +244 -0
- package/dist-standalone/serialization.js +1 -0
- package/dist-standalone/split-channel.d.ts +49 -1
- package/dist-standalone/split-channel.js +1 -171
- package/dist-standalone/subscription-proof.js +1 -224
- package/dist-standalone/succession.js +1 -142
- package/dist-standalone/timeouts.d.ts +275 -0
- package/dist-standalone/timeouts.js +1 -0
- package/dist-standalone/trace-context.d.ts +252 -0
- package/dist-standalone/trace-context.js +1 -0
- package/dist-standalone/trace-spans.d.ts +360 -0
- package/dist-standalone/trace-spans.js +1 -0
- package/dist-standalone/transport.js +1 -59
- package/dist-standalone/trust-registry.d.ts +106 -5
- package/dist-standalone/trust-registry.js +1 -702
- package/dist-standalone/vault-auth.d.ts +91 -0
- package/dist-standalone/vault-auth.js +1 -0
- package/dist-standalone/vault-store-loader.d.ts +110 -0
- package/dist-standalone/vault-store-loader.js +1 -0
- package/dist-standalone/verify.js +1 -16
- package/dist-standalone/version-info.d.ts +259 -0
- package/dist-standalone/version-info.js +1 -0
- package/dist-standalone/xfetch.js +1 -247
- package/llms.txt +1 -0
- package/package.json +65 -5
- package/share1.dat +0 -0
- package/dist-standalone/_deps/crypto/base64.d.ts +0 -29
- package/dist-standalone/_deps/crypto/base64.js +0 -222
- package/dist-standalone/_deps/crypto/cjs/base64.js +0 -665
- package/dist-standalone/_deps/crypto/cjs/errors.js +0 -675
- package/dist-standalone/_deps/crypto/cjs/hmac.js +0 -473
- package/dist-standalone/_deps/crypto/cjs/index.js +0 -852
- package/dist-standalone/_deps/crypto/cjs/package.json +0 -1
- package/dist-standalone/_deps/crypto/cjs/padding.js +0 -511
- package/dist-standalone/_deps/crypto/cjs/share-header.js +0 -372
- package/dist-standalone/_deps/crypto/cjs/shares.js +0 -874
- package/dist-standalone/_deps/crypto/cjs/tlv.js +0 -1021
- package/dist-standalone/_deps/crypto/cjs/uuid.js +0 -443
- package/dist-standalone/_deps/crypto/cjs/verify.js +0 -414
- package/dist-standalone/_deps/crypto/cjs/xorida.js +0 -923
- package/dist-standalone/_deps/crypto/errors.d.ts +0 -51
- package/dist-standalone/_deps/crypto/errors.js +0 -199
- package/dist-standalone/_deps/crypto/hmac.d.ts +0 -39
- package/dist-standalone/_deps/crypto/hmac.js +0 -134
- package/dist-standalone/_deps/crypto/index.d.ts +0 -20
- package/dist-standalone/_deps/crypto/index.js +0 -145
- package/dist-standalone/_deps/crypto/padding.d.ts +0 -19
- package/dist-standalone/_deps/crypto/padding.js +0 -159
- package/dist-standalone/_deps/crypto/share-header.d.ts +0 -44
- package/dist-standalone/_deps/crypto/share-header.js +0 -92
- package/dist-standalone/_deps/crypto/shares.d.ts +0 -27
- package/dist-standalone/_deps/crypto/shares.js +0 -295
- package/dist-standalone/_deps/crypto/tlv.d.ts +0 -26
- package/dist-standalone/_deps/crypto/tlv.js +0 -364
- package/dist-standalone/_deps/crypto/uuid.d.ts +0 -22
- package/dist-standalone/_deps/crypto/uuid.js +0 -136
- package/dist-standalone/_deps/crypto/verify.d.ts +0 -15
- package/dist-standalone/_deps/crypto/verify.js +0 -71
- package/dist-standalone/_deps/crypto/xorida.d.ts +0 -44
- package/dist-standalone/_deps/crypto/xorida.js +0 -366
- package/dist-standalone/_deps/shared/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/shared/errors.js.map +0 -1
- package/dist-standalone/_deps/shared/index.d.ts.map +0 -1
- package/dist-standalone/_deps/shared/index.js.map +0 -1
- package/dist-standalone/_deps/shared/types.d.ts.map +0 -1
- package/dist-standalone/_deps/shared/types.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/errors.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/index.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/index.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/pagination.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/progress.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/progress.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/search.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/search.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/types.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/types.js.map +0 -1
- package/dist-standalone/_deps/xregistry/discovery.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/discovery.js.map +0 -1
- package/dist-standalone/_deps/xregistry/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/errors.js.map +0 -1
- package/dist-standalone/_deps/xregistry/index.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/index.js.map +0 -1
- package/dist-standalone/_deps/xregistry/registry.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/registry.js.map +0 -1
- package/dist-standalone/_deps/xregistry/schema.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/schema.js.map +0 -1
- package/dist-standalone/_deps/xregistry/types.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/types.js.map +0 -1
|
@@ -1,702 +1 @@
|
|
|
1
|
-
import { ok, err } from"./_deps/shared/index.js";
|
|
2
|
-
import * as fs from 'node:fs/promises';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
/* ── Memory Implementation ── */
|
|
5
|
-
/**
|
|
6
|
-
* In-memory trust registry for development and testing.
|
|
7
|
-
*/
|
|
8
|
-
export class MemoryTrustRegistry {
|
|
9
|
-
entries = new Map();
|
|
10
|
-
async register(did, publicKey, name, scopes, x25519PublicKey, mlKemPublicKey, mlDsaPublicKey, xchange, receiveScopes) {
|
|
11
|
-
if (this.entries.has(did))
|
|
12
|
-
return err('ALREADY_REGISTERED');
|
|
13
|
-
this.entries.set(did, {
|
|
14
|
-
did,
|
|
15
|
-
publicKey,
|
|
16
|
-
name,
|
|
17
|
-
scopes: new Set(scopes ?? []),
|
|
18
|
-
receiveScopes: receiveScopes ? new Set(receiveScopes) : undefined,
|
|
19
|
-
revoked: false,
|
|
20
|
-
rotation_sequence: 1, // Initial sequence starts at 1
|
|
21
|
-
x25519PublicKey,
|
|
22
|
-
mlKemPublicKey,
|
|
23
|
-
mlDsaPublicKey,
|
|
24
|
-
xchange,
|
|
25
|
-
});
|
|
26
|
-
return ok(undefined);
|
|
27
|
-
}
|
|
28
|
-
async resolve(did) {
|
|
29
|
-
const entry = this.entries.get(did);
|
|
30
|
-
if (!entry)
|
|
31
|
-
return err('NOT_FOUND');
|
|
32
|
-
if (entry.revoked)
|
|
33
|
-
return err('REVOKED');
|
|
34
|
-
return ok(entry.publicKey);
|
|
35
|
-
}
|
|
36
|
-
async hasScope(did, scope) {
|
|
37
|
-
const entry = this.entries.get(did);
|
|
38
|
-
if (!entry || entry.revoked)
|
|
39
|
-
return false;
|
|
40
|
-
return entry.scopes.has(scope);
|
|
41
|
-
}
|
|
42
|
-
async hasReceiveScope(did, scope) {
|
|
43
|
-
const entry = this.entries.get(did);
|
|
44
|
-
if (!entry || entry.revoked)
|
|
45
|
-
return false;
|
|
46
|
-
// Undefined = accept all scopes (backward compatibility)
|
|
47
|
-
if (!entry.receiveScopes)
|
|
48
|
-
return true;
|
|
49
|
-
return entry.receiveScopes.has(scope);
|
|
50
|
-
}
|
|
51
|
-
async revoke(did) {
|
|
52
|
-
const entry = this.entries.get(did);
|
|
53
|
-
if (!entry)
|
|
54
|
-
return err('NOT_FOUND');
|
|
55
|
-
this.entries.set(did, { ...entry, revoked: true });
|
|
56
|
-
return ok(undefined);
|
|
57
|
-
}
|
|
58
|
-
async getEntry(did) {
|
|
59
|
-
const entry = this.entries.get(did);
|
|
60
|
-
if (!entry)
|
|
61
|
-
return err('NOT_FOUND');
|
|
62
|
-
return ok(entry);
|
|
63
|
-
}
|
|
64
|
-
async updateScopes(did, scopes) {
|
|
65
|
-
const entry = this.entries.get(did);
|
|
66
|
-
if (!entry)
|
|
67
|
-
return err('NOT_FOUND');
|
|
68
|
-
if (entry.revoked)
|
|
69
|
-
return err('REVOKED');
|
|
70
|
-
this.entries.set(did, { ...entry, scopes: new Set(scopes) });
|
|
71
|
-
return ok(undefined);
|
|
72
|
-
}
|
|
73
|
-
/** Number of entries (for testing). */
|
|
74
|
-
get size() {
|
|
75
|
-
return this.entries.size;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* HTTP-backed trust registry for production use.
|
|
80
|
-
* Delegates to a remote atelier.xail.io service.
|
|
81
|
-
*/
|
|
82
|
-
export class HttpTrustRegistry {
|
|
83
|
-
baseUrl;
|
|
84
|
-
fetchFn;
|
|
85
|
-
cacheTtlMs;
|
|
86
|
-
cacheFailureMode;
|
|
87
|
-
enablePush;
|
|
88
|
-
bloomFilterSize;
|
|
89
|
-
bloomFilterFpr;
|
|
90
|
-
resolveCache = new Map();
|
|
91
|
-
entryCache = new Map();
|
|
92
|
-
constructor(opts) {
|
|
93
|
-
this.baseUrl = opts.baseUrl.replace(/\/$/, '');
|
|
94
|
-
this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
95
|
-
this.cacheTtlMs = opts.cacheTtlMs ?? 30_000;
|
|
96
|
-
this.cacheFailureMode = opts.cacheFailureMode ?? 'fail-secure';
|
|
97
|
-
this.enablePush = opts.enablePush ?? false;
|
|
98
|
-
this.bloomFilterSize = opts.bloomFilterSize ?? 10_000;
|
|
99
|
-
this.bloomFilterFpr = opts.bloomFilterFpr ?? 0.01;
|
|
100
|
-
}
|
|
101
|
-
/** Clear all cached entries. Call after registration or revocation. */
|
|
102
|
-
clearCache() {
|
|
103
|
-
this.resolveCache.clear();
|
|
104
|
-
this.entryCache.clear();
|
|
105
|
-
}
|
|
106
|
-
async register(did, publicKey, name, scopes, x25519PublicKey, mlKemPublicKey, mlDsaPublicKey, xchange, receiveScopes) {
|
|
107
|
-
try {
|
|
108
|
-
const res = await this.fetchFn(`${this.baseUrl}/registry/register`, {
|
|
109
|
-
method: 'POST',
|
|
110
|
-
headers: { 'Content-Type': 'application/json' },
|
|
111
|
-
body: JSON.stringify({
|
|
112
|
-
did,
|
|
113
|
-
publicKey: Array.from(publicKey),
|
|
114
|
-
name,
|
|
115
|
-
scopes: scopes ?? [],
|
|
116
|
-
...(receiveScopes
|
|
117
|
-
? { receiveScopes }
|
|
118
|
-
: {}),
|
|
119
|
-
...(x25519PublicKey
|
|
120
|
-
? { x25519PublicKey: Array.from(x25519PublicKey) }
|
|
121
|
-
: {}),
|
|
122
|
-
...(mlKemPublicKey
|
|
123
|
-
? { mlKemPublicKey: Array.from(mlKemPublicKey) }
|
|
124
|
-
: {}),
|
|
125
|
-
...(mlDsaPublicKey
|
|
126
|
-
? { mlDsaPublicKey: Array.from(mlDsaPublicKey) }
|
|
127
|
-
: {}),
|
|
128
|
-
...(xchange !== undefined
|
|
129
|
-
? { xchange }
|
|
130
|
-
: {}),
|
|
131
|
-
}),
|
|
132
|
-
});
|
|
133
|
-
if (res.status === 409)
|
|
134
|
-
return err('ALREADY_REGISTERED');
|
|
135
|
-
if (!res.ok)
|
|
136
|
-
return err('NETWORK_ERROR');
|
|
137
|
-
return ok(undefined);
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return err('NETWORK_ERROR');
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
async resolve(did) {
|
|
144
|
-
// Check cache first
|
|
145
|
-
if (this.cacheTtlMs > 0) {
|
|
146
|
-
const cached = this.resolveCache.get(did);
|
|
147
|
-
if (cached && cached.expiry > Date.now())
|
|
148
|
-
return cached.value;
|
|
149
|
-
}
|
|
150
|
-
let result;
|
|
151
|
-
try {
|
|
152
|
-
const res = await this.fetchFn(`${this.baseUrl}/registry/resolve/${encodeURIComponent(did)}`);
|
|
153
|
-
if (res.status === 404)
|
|
154
|
-
result = err('NOT_FOUND');
|
|
155
|
-
else if (res.status === 410)
|
|
156
|
-
result = err('REVOKED');
|
|
157
|
-
else if (!res.ok)
|
|
158
|
-
result = err('NETWORK_ERROR');
|
|
159
|
-
else {
|
|
160
|
-
const data = (await res.json());
|
|
161
|
-
result = ok(new Uint8Array(data.publicKey));
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
catch {
|
|
165
|
-
// Network failure - apply cache failure mode
|
|
166
|
-
const staleCache = this.resolveCache.get(did);
|
|
167
|
-
if (this.cacheFailureMode === 'fail-secure') {
|
|
168
|
-
// Fail-secure: reject on cache refresh failure (default)
|
|
169
|
-
result = err('NETWORK_ERROR');
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
// Fail-open: accept stale cache if available
|
|
173
|
-
if (staleCache) {
|
|
174
|
-
// Return stale cached value
|
|
175
|
-
return staleCache.value;
|
|
176
|
-
}
|
|
177
|
-
result = err('NETWORK_ERROR');
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
// Update cache with fresh result (only if cache enabled)
|
|
181
|
-
if (this.cacheTtlMs > 0) {
|
|
182
|
-
this.resolveCache.set(did, { value: result, expiry: Date.now() + this.cacheTtlMs });
|
|
183
|
-
}
|
|
184
|
-
return result;
|
|
185
|
-
}
|
|
186
|
-
async hasScope(did, scope) {
|
|
187
|
-
try {
|
|
188
|
-
const res = await this.fetchFn(`${this.baseUrl}/registry/scope/${encodeURIComponent(did)}/${encodeURIComponent(scope)}`);
|
|
189
|
-
return res.ok;
|
|
190
|
-
}
|
|
191
|
-
catch {
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
async hasReceiveScope(did, scope) {
|
|
196
|
-
try {
|
|
197
|
-
const res = await this.fetchFn(`${this.baseUrl}/registry/receive-scope/${encodeURIComponent(did)}/${encodeURIComponent(scope)}`);
|
|
198
|
-
return res.ok;
|
|
199
|
-
}
|
|
200
|
-
catch {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
async revoke(did) {
|
|
205
|
-
try {
|
|
206
|
-
const res = await this.fetchFn(`${this.baseUrl}/registry/revoke/${encodeURIComponent(did)}`, { method: 'POST' });
|
|
207
|
-
if (res.status === 404)
|
|
208
|
-
return err('NOT_FOUND');
|
|
209
|
-
if (!res.ok)
|
|
210
|
-
return err('NETWORK_ERROR');
|
|
211
|
-
return ok(undefined);
|
|
212
|
-
}
|
|
213
|
-
catch {
|
|
214
|
-
return err('NETWORK_ERROR');
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
async getEntry(did) {
|
|
218
|
-
if (this.cacheTtlMs > 0) {
|
|
219
|
-
const cached = this.entryCache.get(did);
|
|
220
|
-
if (cached && cached.expiry > Date.now())
|
|
221
|
-
return cached.value;
|
|
222
|
-
}
|
|
223
|
-
let result;
|
|
224
|
-
try {
|
|
225
|
-
const res = await this.fetchFn(`${this.baseUrl}/registry/entry/${encodeURIComponent(did)}`);
|
|
226
|
-
if (res.status === 404)
|
|
227
|
-
result = err('NOT_FOUND');
|
|
228
|
-
else if (!res.ok)
|
|
229
|
-
result = err('NETWORK_ERROR');
|
|
230
|
-
else {
|
|
231
|
-
const data = (await res.json());
|
|
232
|
-
result = ok({
|
|
233
|
-
did: data.did,
|
|
234
|
-
publicKey: new Uint8Array(data.publicKey),
|
|
235
|
-
name: data.name,
|
|
236
|
-
scopes: new Set(data.scopes),
|
|
237
|
-
receiveScopes: data.receiveScopes ? new Set(data.receiveScopes) : undefined,
|
|
238
|
-
revoked: data.revoked,
|
|
239
|
-
rotation_sequence: data.rotation_sequence ?? 1,
|
|
240
|
-
x25519PublicKey: data.x25519PublicKey
|
|
241
|
-
? new Uint8Array(data.x25519PublicKey)
|
|
242
|
-
: undefined,
|
|
243
|
-
mlKemPublicKey: data.mlKemPublicKey
|
|
244
|
-
? new Uint8Array(data.mlKemPublicKey)
|
|
245
|
-
: undefined,
|
|
246
|
-
mlDsaPublicKey: data.mlDsaPublicKey
|
|
247
|
-
? new Uint8Array(data.mlDsaPublicKey)
|
|
248
|
-
: undefined,
|
|
249
|
-
xchange: data.xchange,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
catch {
|
|
254
|
-
result = err('NETWORK_ERROR');
|
|
255
|
-
}
|
|
256
|
-
if (this.cacheTtlMs > 0) {
|
|
257
|
-
this.entryCache.set(did, { value: result, expiry: Date.now() + this.cacheTtlMs });
|
|
258
|
-
}
|
|
259
|
-
return result;
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Rotate a DID to a new public key with cryptographic proof.
|
|
263
|
-
*
|
|
264
|
-
* Sends rotation request to gateway and invalidates local cache.
|
|
265
|
-
*
|
|
266
|
-
* @param did - DID being rotated (old key)
|
|
267
|
-
* @param newPublicKey - New public key bytes
|
|
268
|
-
* @param proof - Succession announcement (dual signatures from old and new keys)
|
|
269
|
-
* @param rotationSequence - Monotonically increasing sequence number (prevents rollback)
|
|
270
|
-
*/
|
|
271
|
-
async rotate(did, newPublicKey, proof, rotationSequence) {
|
|
272
|
-
const res = await this.fetchFn(`${this.baseUrl}/registry/rotate`, {
|
|
273
|
-
method: 'POST',
|
|
274
|
-
headers: { 'Content-Type': 'application/json' },
|
|
275
|
-
body: JSON.stringify({
|
|
276
|
-
did,
|
|
277
|
-
newPublicKey: Array.from(newPublicKey),
|
|
278
|
-
proof: Array.from(proof),
|
|
279
|
-
rotationSequence,
|
|
280
|
-
}),
|
|
281
|
-
});
|
|
282
|
-
if (!res.ok) {
|
|
283
|
-
throw new Error(`Key rotation failed: ${res.status} ${res.statusText}`);
|
|
284
|
-
}
|
|
285
|
-
// Invalidate cache for this DID
|
|
286
|
-
this.resolveCache.delete(did);
|
|
287
|
-
this.entryCache.delete(did);
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Subscribe to real-time trust events (revocation, key rotation).
|
|
291
|
-
*
|
|
292
|
-
* Creates a bloom filter from DIDs and connects WebSocket to gateway.
|
|
293
|
-
* Events matching subscribed DIDs trigger the callback and invalidate cache.
|
|
294
|
-
*
|
|
295
|
-
* @param dids - Array of DIDs to monitor
|
|
296
|
-
* @param callback - Function called when trust events occur
|
|
297
|
-
* @returns Unsubscribe function to stop watching
|
|
298
|
-
*
|
|
299
|
-
* @throws Error if push notifications not enabled (enablePush: true)
|
|
300
|
-
*/
|
|
301
|
-
async subscribe(dids, callback) {
|
|
302
|
-
if (!this.enablePush) {
|
|
303
|
-
throw new Error('Push notifications not enabled (set enablePush: true in HttpTrustRegistryOptions)');
|
|
304
|
-
}
|
|
305
|
-
// Simple bloom filter: hash each DID to track subscriptions
|
|
306
|
-
// Production implementation would use full bloom filter library
|
|
307
|
-
const bloomSet = new Set(dids.map(did => this.hashDid(did)));
|
|
308
|
-
// Convert HTTP URL to WebSocket URL
|
|
309
|
-
const wsUrl = this.baseUrl.replace(/^http/, 'ws') + '/trust/events';
|
|
310
|
-
// SAFETY: WebSocket is a standard browser/Node.js API
|
|
311
|
-
const ws = new globalThis.WebSocket(wsUrl);
|
|
312
|
-
ws.addEventListener('open', () => {
|
|
313
|
-
// Send subscription with bloom filter
|
|
314
|
-
ws.send(JSON.stringify({
|
|
315
|
-
type: 'subscribe',
|
|
316
|
-
dids: dids, // Simple implementation sends full DID list
|
|
317
|
-
bloomSize: this.bloomFilterSize,
|
|
318
|
-
bloomFpr: this.bloomFilterFpr,
|
|
319
|
-
}));
|
|
320
|
-
});
|
|
321
|
-
ws.addEventListener('message', (event) => {
|
|
322
|
-
try {
|
|
323
|
-
const trustEvent = JSON.parse(event.data);
|
|
324
|
-
// Check if event DID matches our subscription
|
|
325
|
-
const eventHash = this.hashDid(trustEvent.did);
|
|
326
|
-
if (bloomSet.has(eventHash)) {
|
|
327
|
-
// Invalidate cache for this DID
|
|
328
|
-
if (trustEvent.type === 'revocation' || trustEvent.type === 'succession') {
|
|
329
|
-
this.resolveCache.delete(trustEvent.did);
|
|
330
|
-
this.entryCache.delete(trustEvent.did);
|
|
331
|
-
}
|
|
332
|
-
// Notify callback
|
|
333
|
-
callback(trustEvent);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
catch (error) {
|
|
337
|
-
// Ignore malformed events
|
|
338
|
-
console.warn('Failed to parse trust event:', error);
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
ws.addEventListener('error', (error) => {
|
|
342
|
-
console.error('WebSocket error:', error);
|
|
343
|
-
});
|
|
344
|
-
// Return unsubscribe function
|
|
345
|
-
return () => {
|
|
346
|
-
if (ws.readyState === globalThis.WebSocket.OPEN) {
|
|
347
|
-
ws.close();
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Simple hash function for bloom filter (DID → number).
|
|
353
|
-
* Production implementation would use proper bloom filter hashing.
|
|
354
|
-
*/
|
|
355
|
-
hashDid(did) {
|
|
356
|
-
let hash = 0;
|
|
357
|
-
for (let i = 0; i < did.length; i++) {
|
|
358
|
-
hash = ((hash << 5) - hash) + did.charCodeAt(i);
|
|
359
|
-
hash = hash & hash; // Convert to 32-bit integer
|
|
360
|
-
}
|
|
361
|
-
return hash;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Resume subscriptions on this gateway using proofs from another gateway.
|
|
365
|
-
*
|
|
366
|
-
* Allows clients to migrate between gateways without re-subscribing.
|
|
367
|
-
* Validates proofs and restores subscription state.
|
|
368
|
-
*
|
|
369
|
-
* @param proofs - Array of subscription proofs from previous gateway.
|
|
370
|
-
* @returns Success or error.
|
|
371
|
-
*
|
|
372
|
-
* @example
|
|
373
|
-
* ```typescript
|
|
374
|
-
* const registry = new HttpTrustRegistry({ baseUrl: 'https://atelier2.xail.io' });
|
|
375
|
-
* const result = await registry.resumeSubscriptions([proof1, proof2]);
|
|
376
|
-
* ```
|
|
377
|
-
*/
|
|
378
|
-
async resumeSubscriptions(proofs) {
|
|
379
|
-
try {
|
|
380
|
-
const res = await this.fetchFn(`${this.baseUrl}/trust/resume-batch`, {
|
|
381
|
-
method: 'POST',
|
|
382
|
-
headers: { 'Content-Type': 'application/json' },
|
|
383
|
-
body: JSON.stringify({ proofs }),
|
|
384
|
-
});
|
|
385
|
-
if (!res.ok)
|
|
386
|
-
return err('NETWORK_ERROR');
|
|
387
|
-
return ok(undefined);
|
|
388
|
-
}
|
|
389
|
-
catch {
|
|
390
|
-
return err('NETWORK_ERROR');
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Fetch signed checkpoint for a DID (freshness primitive).
|
|
395
|
-
*
|
|
396
|
-
* Checkpoints provide cryptographic proof of DID state at a specific timestamp.
|
|
397
|
-
* Clients verify checkpoint signature and compare rotation_sequence to detect staleness.
|
|
398
|
-
*
|
|
399
|
-
* @param did - DID to fetch checkpoint for
|
|
400
|
-
* @returns Signed checkpoint or error
|
|
401
|
-
*
|
|
402
|
-
* @example
|
|
403
|
-
* ```typescript
|
|
404
|
-
* const checkpoint = await registry.fetchCheckpoint('did:key:z6Mk...');
|
|
405
|
-
* if (checkpoint.ok) {
|
|
406
|
-
* const verified = await verifyCheckpoint(checkpoint.value, gatewayPubKey);
|
|
407
|
-
* if (verified.ok && verified.value) {
|
|
408
|
-
* // Use checkpoint for staleness detection
|
|
409
|
-
* if (isCacheStale(localCache, checkpoint.value)) {
|
|
410
|
-
* // Refresh cache
|
|
411
|
-
* }
|
|
412
|
-
* }
|
|
413
|
-
* }
|
|
414
|
-
* ```
|
|
415
|
-
*/
|
|
416
|
-
async fetchCheckpoint(did) {
|
|
417
|
-
try {
|
|
418
|
-
const res = await this.fetchFn(`${this.baseUrl}/registry/checkpoint/${encodeURIComponent(did)}`);
|
|
419
|
-
if (res.status === 404)
|
|
420
|
-
return err('NOT_FOUND');
|
|
421
|
-
if (!res.ok)
|
|
422
|
-
return err('NETWORK_ERROR');
|
|
423
|
-
const checkpoint = (await res.json());
|
|
424
|
-
return ok(checkpoint);
|
|
425
|
-
}
|
|
426
|
-
catch {
|
|
427
|
-
return err('NETWORK_ERROR');
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
async updateScopes(did, scopes) {
|
|
431
|
-
try {
|
|
432
|
-
const res = await this.fetchFn(`${this.baseUrl}/registry/${encodeURIComponent(did)}/scopes`, {
|
|
433
|
-
method: 'POST',
|
|
434
|
-
headers: { 'Content-Type': 'application/json' },
|
|
435
|
-
body: JSON.stringify({ scopes }),
|
|
436
|
-
});
|
|
437
|
-
if (res.status === 404)
|
|
438
|
-
return err('NOT_FOUND');
|
|
439
|
-
if (res.status === 410)
|
|
440
|
-
return err('REVOKED');
|
|
441
|
-
if (!res.ok)
|
|
442
|
-
return err('NETWORK_ERROR');
|
|
443
|
-
// Clear cache for this DID
|
|
444
|
-
this.resolveCache.delete(did);
|
|
445
|
-
this.entryCache.delete(did);
|
|
446
|
-
return ok(undefined);
|
|
447
|
-
}
|
|
448
|
-
catch {
|
|
449
|
-
return err('NETWORK_ERROR');
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* File-based trust registry using JSONL append-only log.
|
|
455
|
-
* Replays all entries on initialization, keeps in-memory Map for fast access.
|
|
456
|
-
* Suitable for production deployments with local persistence.
|
|
457
|
-
*/
|
|
458
|
-
export class FileTrustRegistry {
|
|
459
|
-
path;
|
|
460
|
-
entries = new Map();
|
|
461
|
-
initialized = false;
|
|
462
|
-
constructor(opts) {
|
|
463
|
-
this.path = opts.path;
|
|
464
|
-
}
|
|
465
|
-
/** Initialize by replaying JSONL log. Called automatically on first operation. */
|
|
466
|
-
async init() {
|
|
467
|
-
if (this.initialized)
|
|
468
|
-
return;
|
|
469
|
-
try {
|
|
470
|
-
// Ensure directory exists
|
|
471
|
-
await fs.mkdir(path.dirname(this.path), { recursive: true });
|
|
472
|
-
// Read and replay JSONL file
|
|
473
|
-
const content = await fs.readFile(this.path, 'utf-8').catch(() => '');
|
|
474
|
-
const lines = content.split('\n').filter((line) => line.trim());
|
|
475
|
-
for (const line of lines) {
|
|
476
|
-
const record = JSON.parse(line);
|
|
477
|
-
if (record.type === 'register' && record.publicKey && record.name) {
|
|
478
|
-
this.entries.set(record.did, {
|
|
479
|
-
did: record.did,
|
|
480
|
-
publicKey: new Uint8Array(record.publicKey),
|
|
481
|
-
name: record.name,
|
|
482
|
-
scopes: new Set(record.scopes ?? []),
|
|
483
|
-
receiveScopes: record.receiveScopes ? new Set(record.receiveScopes) : undefined,
|
|
484
|
-
revoked: false,
|
|
485
|
-
rotation_sequence: record.rotation_sequence ?? 1,
|
|
486
|
-
x25519PublicKey: record.x25519PublicKey
|
|
487
|
-
? new Uint8Array(record.x25519PublicKey)
|
|
488
|
-
: undefined,
|
|
489
|
-
mlKemPublicKey: record.mlKemPublicKey
|
|
490
|
-
? new Uint8Array(record.mlKemPublicKey)
|
|
491
|
-
: undefined,
|
|
492
|
-
mlDsaPublicKey: record.mlDsaPublicKey
|
|
493
|
-
? new Uint8Array(record.mlDsaPublicKey)
|
|
494
|
-
: undefined,
|
|
495
|
-
xchange: record.xchange,
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
else if (record.type === 'revoke') {
|
|
499
|
-
const entry = this.entries.get(record.did);
|
|
500
|
-
if (entry) {
|
|
501
|
-
this.entries.set(record.did, { ...entry, revoked: true });
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
else if (record.type === 'update-scopes') {
|
|
505
|
-
const entry = this.entries.get(record.did);
|
|
506
|
-
if (entry) {
|
|
507
|
-
this.entries.set(record.did, { ...entry, scopes: new Set(record.scopes ?? []) });
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
else if (record.type === 'rotate' && record.publicKey && record.rotation_sequence) {
|
|
511
|
-
const entry = this.entries.get(record.did);
|
|
512
|
-
if (entry) {
|
|
513
|
-
// Only apply rotation if sequence is greater (prevents replaying stale rotations)
|
|
514
|
-
if (record.rotation_sequence > entry.rotation_sequence) {
|
|
515
|
-
this.entries.set(record.did, {
|
|
516
|
-
...entry,
|
|
517
|
-
publicKey: new Uint8Array(record.publicKey),
|
|
518
|
-
rotation_sequence: record.rotation_sequence,
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
catch (error) {
|
|
526
|
-
// If file doesn't exist yet, that's OK - it will be created on first write
|
|
527
|
-
}
|
|
528
|
-
this.initialized = true;
|
|
529
|
-
}
|
|
530
|
-
/** Append record to JSONL file. */
|
|
531
|
-
async append(record) {
|
|
532
|
-
await fs.appendFile(this.path, JSON.stringify(record) + '\n', 'utf-8');
|
|
533
|
-
}
|
|
534
|
-
async register(did, publicKey, name, scopes, x25519PublicKey, mlKemPublicKey, mlDsaPublicKey, xchange, receiveScopes) {
|
|
535
|
-
await this.init();
|
|
536
|
-
if (this.entries.has(did))
|
|
537
|
-
return err('ALREADY_REGISTERED');
|
|
538
|
-
const entry = {
|
|
539
|
-
did,
|
|
540
|
-
publicKey,
|
|
541
|
-
name,
|
|
542
|
-
scopes: new Set(scopes ?? []),
|
|
543
|
-
receiveScopes: receiveScopes ? new Set(receiveScopes) : undefined,
|
|
544
|
-
revoked: false,
|
|
545
|
-
rotation_sequence: 1, // Initial sequence starts at 1
|
|
546
|
-
x25519PublicKey,
|
|
547
|
-
mlKemPublicKey,
|
|
548
|
-
mlDsaPublicKey,
|
|
549
|
-
xchange,
|
|
550
|
-
};
|
|
551
|
-
this.entries.set(did, entry);
|
|
552
|
-
// Append to JSONL
|
|
553
|
-
await this.append({
|
|
554
|
-
type: 'register',
|
|
555
|
-
did,
|
|
556
|
-
publicKey: Array.from(publicKey),
|
|
557
|
-
name,
|
|
558
|
-
scopes: scopes ?? [],
|
|
559
|
-
rotation_sequence: 1,
|
|
560
|
-
...(receiveScopes ? { receiveScopes } : {}),
|
|
561
|
-
...(x25519PublicKey ? { x25519PublicKey: Array.from(x25519PublicKey) } : {}),
|
|
562
|
-
...(mlKemPublicKey ? { mlKemPublicKey: Array.from(mlKemPublicKey) } : {}),
|
|
563
|
-
...(mlDsaPublicKey ? { mlDsaPublicKey: Array.from(mlDsaPublicKey) } : {}),
|
|
564
|
-
...(xchange !== undefined ? { xchange } : {}),
|
|
565
|
-
});
|
|
566
|
-
return ok(undefined);
|
|
567
|
-
}
|
|
568
|
-
async resolve(did) {
|
|
569
|
-
await this.init();
|
|
570
|
-
const entry = this.entries.get(did);
|
|
571
|
-
if (!entry)
|
|
572
|
-
return err('NOT_FOUND');
|
|
573
|
-
if (entry.revoked)
|
|
574
|
-
return err('REVOKED');
|
|
575
|
-
return ok(entry.publicKey);
|
|
576
|
-
}
|
|
577
|
-
async hasScope(did, scope) {
|
|
578
|
-
await this.init();
|
|
579
|
-
const entry = this.entries.get(did);
|
|
580
|
-
if (!entry || entry.revoked)
|
|
581
|
-
return false;
|
|
582
|
-
return entry.scopes.has(scope);
|
|
583
|
-
}
|
|
584
|
-
async hasReceiveScope(did, scope) {
|
|
585
|
-
await this.init();
|
|
586
|
-
const entry = this.entries.get(did);
|
|
587
|
-
if (!entry || entry.revoked)
|
|
588
|
-
return false;
|
|
589
|
-
// Undefined = accept all scopes (backward compatibility)
|
|
590
|
-
if (!entry.receiveScopes)
|
|
591
|
-
return true;
|
|
592
|
-
return entry.receiveScopes.has(scope);
|
|
593
|
-
}
|
|
594
|
-
async revoke(did) {
|
|
595
|
-
await this.init();
|
|
596
|
-
const entry = this.entries.get(did);
|
|
597
|
-
if (!entry)
|
|
598
|
-
return err('NOT_FOUND');
|
|
599
|
-
this.entries.set(did, { ...entry, revoked: true });
|
|
600
|
-
await this.append({ type: 'revoke', did });
|
|
601
|
-
return ok(undefined);
|
|
602
|
-
}
|
|
603
|
-
async getEntry(did) {
|
|
604
|
-
await this.init();
|
|
605
|
-
const entry = this.entries.get(did);
|
|
606
|
-
if (!entry)
|
|
607
|
-
return err('NOT_FOUND');
|
|
608
|
-
return ok(entry);
|
|
609
|
-
}
|
|
610
|
-
async updateScopes(did, scopes) {
|
|
611
|
-
await this.init();
|
|
612
|
-
const entry = this.entries.get(did);
|
|
613
|
-
if (!entry)
|
|
614
|
-
return err('NOT_FOUND');
|
|
615
|
-
if (entry.revoked)
|
|
616
|
-
return err('REVOKED');
|
|
617
|
-
this.entries.set(did, { ...entry, scopes: new Set(scopes) });
|
|
618
|
-
await this.append({ type: 'update-scopes', did, scopes });
|
|
619
|
-
return ok(undefined);
|
|
620
|
-
}
|
|
621
|
-
/**
|
|
622
|
-
* Rotate a DID to a new public key with rollback protection.
|
|
623
|
-
*
|
|
624
|
-
* Validates that rotationSequence is greater than current sequence to prevent
|
|
625
|
-
* rollback attacks. Appends rotation event to JSONL and updates in-memory state.
|
|
626
|
-
*
|
|
627
|
-
* @param did - DID being rotated
|
|
628
|
-
* @param newPublicKey - New public key bytes
|
|
629
|
-
* @param proof - Cryptographic proof (e.g., signature from old key)
|
|
630
|
-
* @param rotationSequence - Monotonically increasing sequence number
|
|
631
|
-
* @throws Error if DID not found or sequence validation fails
|
|
632
|
-
*/
|
|
633
|
-
async rotate(did, newPublicKey, proof, rotationSequence) {
|
|
634
|
-
await this.init();
|
|
635
|
-
const entry = this.entries.get(did);
|
|
636
|
-
if (!entry) {
|
|
637
|
-
throw new Error(`DID not found: ${did}`);
|
|
638
|
-
}
|
|
639
|
-
// Rollback protection: new sequence must be greater than current
|
|
640
|
-
if (rotationSequence <= entry.rotation_sequence) {
|
|
641
|
-
throw new Error(`Rotation sequence ${rotationSequence} must be > current ${entry.rotation_sequence} (rollback attack prevented)`);
|
|
642
|
-
}
|
|
643
|
-
// Append rotation event to JSONL
|
|
644
|
-
await this.append({
|
|
645
|
-
type: 'rotate',
|
|
646
|
-
did,
|
|
647
|
-
publicKey: Array.from(newPublicKey),
|
|
648
|
-
proof: Array.from(proof),
|
|
649
|
-
rotation_sequence: rotationSequence,
|
|
650
|
-
timestamp: Date.now(),
|
|
651
|
-
});
|
|
652
|
-
// Update in-memory entry
|
|
653
|
-
this.entries.set(did, {
|
|
654
|
-
...entry,
|
|
655
|
-
publicKey: newPublicKey,
|
|
656
|
-
rotation_sequence: rotationSequence,
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
/** Number of entries (for testing). */
|
|
660
|
-
get size() {
|
|
661
|
-
return this.entries.size;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
/* ── Enterprise Factory ── */
|
|
665
|
-
/**
|
|
666
|
-
* Create an enterprise trust registry with optional pre-population.
|
|
667
|
-
* Suitable for corporate deployments with centralized trust management.
|
|
668
|
-
*
|
|
669
|
-
* @example
|
|
670
|
-
* ```typescript
|
|
671
|
-
* const registry = await TrustRegistry.enterprise({
|
|
672
|
-
* storage: 'file',
|
|
673
|
-
* path: '/opt/corp/trust.jsonl',
|
|
674
|
-
* preload: [
|
|
675
|
-
* { did: 'did:web:corp.example.com', publicKey: ..., name: 'Corporate Gateway' }
|
|
676
|
-
* ]
|
|
677
|
-
* });
|
|
678
|
-
* ```
|
|
679
|
-
*/
|
|
680
|
-
export async function createEnterpriseTrustRegistry(opts) {
|
|
681
|
-
let registry;
|
|
682
|
-
if (opts.storage === 'file') {
|
|
683
|
-
if (!opts.path)
|
|
684
|
-
throw new Error('FileTrustRegistry requires path option');
|
|
685
|
-
registry = new FileTrustRegistry({ path: opts.path });
|
|
686
|
-
}
|
|
687
|
-
else if (opts.storage === 'http') {
|
|
688
|
-
if (!opts.baseUrl)
|
|
689
|
-
throw new Error('HttpTrustRegistry requires baseUrl option');
|
|
690
|
-
registry = new HttpTrustRegistry({ baseUrl: opts.baseUrl });
|
|
691
|
-
}
|
|
692
|
-
else {
|
|
693
|
-
registry = new MemoryTrustRegistry();
|
|
694
|
-
}
|
|
695
|
-
// Pre-populate if requested
|
|
696
|
-
if (opts.preload) {
|
|
697
|
-
for (const entry of opts.preload) {
|
|
698
|
-
await registry.register(entry.did, entry.publicKey, entry.name, entry.scopes, entry.x25519PublicKey, entry.mlKemPublicKey, entry.mlDsaPublicKey, entry.xchange, entry.receiveScopes);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
return registry;
|
|
702
|
-
}
|
|
1
|
+
import{ok,err}from"./_deps/shared/index.js";import*as fs from"node:fs/promises";import*as path from"node:path";function isExpired(e){return!!e.expiresAt&&Date.now()>e.expiresAt}export class RegistrationRateLimiter{perIPTimestamps=new Map;globalTimestamps=[];perIPLimit;globalLimit;windowMs;cleanupInterval=null;constructor(e=10,t=1e3,i=36e5){this.perIPLimit=e,this.globalLimit=t,this.windowMs=i,this.cleanupInterval=setInterval(()=>this.cleanup(),3e5)}checkLimit(e){const t=Date.now()-this.windowMs;if((this.perIPTimestamps.get(e)||[]).filter(e=>e>t).length>=this.perIPLimit)return!1;return!(this.globalTimestamps.filter(e=>e>t).length>=this.globalLimit)}recordRegistration(e){const t=Date.now(),i=this.perIPTimestamps.get(e)||[];i.push(t),this.perIPTimestamps.set(e,i),this.globalTimestamps.push(t)}getRemainingForIP(e){const t=Date.now()-this.windowMs,i=(this.perIPTimestamps.get(e)||[]).filter(e=>e>t);return Math.max(0,this.perIPLimit-i.length)}getRemainingGlobal(){const e=Date.now()-this.windowMs,t=this.globalTimestamps.filter(t=>t>e);return Math.max(0,this.globalLimit-t.length)}getResetTimeForIP(e){const t=this.perIPTimestamps.get(e)||[];if(0===t.length)return null;const i=t[0];return i?i+this.windowMs:null}cleanup(){const e=Date.now()-this.windowMs;for(const[t,i]of this.perIPTimestamps.entries()){const s=i.filter(t=>t>e);0===s.length?this.perIPTimestamps.delete(t):this.perIPTimestamps.set(t,s)}const t=this.globalTimestamps.filter(t=>t>e);this.globalTimestamps.length=0,this.globalTimestamps.push(...t)}destroy(){this.cleanupInterval&&(clearInterval(this.cleanupInterval),this.cleanupInterval=null)}reset(){this.perIPTimestamps.clear(),this.globalTimestamps.length=0}}export class MemoryTrustRegistry{entries=new Map;rateLimiter;constructor(e){e?.enableRateLimiting&&(this.rateLimiter=e.rateLimiter||new RegistrationRateLimiter)}async register(e,t,i,s,r,n,o,a,c,l,h,p,u,d){if(this.rateLimiter&&d&&!this.rateLimiter.checkLimit(d))return err("RATE_LIMIT_EXCEEDED");if(this.entries.has(e))return err("ALREADY_REGISTERED");const y=u?Date.now()+u:void 0;return this.entries.set(e,{did:e,publicKey:t,name:i,scopes:new Set(s??[]),receiveScopes:c?new Set(c):void 0,revoked:!1,rotation_sequence:1,x25519PublicKey:r,mlKemPublicKey:n,mlDsaPublicKey:o,xchange:a,sdkVersion:l,minEnvelopeVersion:h,maxEnvelopeVersion:p,expiresAt:y}),this.rateLimiter&&d&&this.rateLimiter.recordRegistration(d),ok(void 0)}async resolve(e){const t=this.entries.get(e);return t?isExpired(t)?err("EXPIRED"):t.revoked?err("REVOKED"):ok(t.publicKey):err("NOT_FOUND")}async hasScope(e,t){const i=this.entries.get(e);return!(!i||isExpired(i)||i.revoked)&&i.scopes.has(t)}async hasReceiveScope(e,t){const i=this.entries.get(e);return!(!i||isExpired(i)||i.revoked)&&(!i.receiveScopes||i.receiveScopes.has(t))}async revoke(e){const t=this.entries.get(e);return t?(this.entries.set(e,{...t,revoked:!0}),ok(void 0)):err("NOT_FOUND")}async getEntry(e){const t=this.entries.get(e);return t?isExpired(t)?err("EXPIRED"):ok(t):err("NOT_FOUND")}async updateScopes(e,t){const i=this.entries.get(e);return i?isExpired(i)?err("EXPIRED"):i.revoked?err("REVOKED"):(this.entries.set(e,{...i,scopes:new Set(t)}),ok(void 0)):err("NOT_FOUND")}async cleanup(){let e=0;const t=Date.now();for(const[i,s]of this.entries)s.expiresAt&&t>s.expiresAt&&(this.entries.delete(i),e++);return e}get size(){return this.entries.size}}export class HttpTrustRegistry{baseUrl;fetchFn;cacheTtlMs;cacheFailureMode;enablePush;bloomFilterSize;bloomFilterFpr;resolveCache=new Map;entryCache=new Map;constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.fetchFn=e.fetch??globalThis.fetch.bind(globalThis),this.cacheTtlMs=e.cacheTtlMs??3e4,this.cacheFailureMode=e.cacheFailureMode??"fail-secure",this.enablePush=e.enablePush??!1,this.bloomFilterSize=e.bloomFilterSize??1e4,this.bloomFilterFpr=e.bloomFilterFpr??.01}clearCache(){this.resolveCache.clear(),this.entryCache.clear()}async register(e,t,i,s,r,n,o,a,c,l,h,p,u,d){try{const d=await this.fetchFn(`${this.baseUrl}/registry/register`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({did:e,publicKey:Array.from(t),name:i,scopes:s??[],...c?{receiveScopes:c}:{},...r?{x25519PublicKey:Array.from(r)}:{},...n?{mlKemPublicKey:Array.from(n)}:{},...o?{mlDsaPublicKey:Array.from(o)}:{},...void 0!==a?{xchange:a}:{},...void 0!==l?{sdkVersion:l}:{},...void 0!==h?{minEnvelopeVersion:h}:{},...void 0!==p?{maxEnvelopeVersion:p}:{},...void 0!==u?{ttlMs:u}:{}})});return 409===d.status?err("ALREADY_REGISTERED"):429===d.status?err("RATE_LIMIT_EXCEEDED"):d.ok?ok(void 0):err("NETWORK_ERROR")}catch{return err("NETWORK_ERROR")}}async resolve(e){if(this.cacheTtlMs>0){const t=this.resolveCache.get(e);if(t&&t.expiry>Date.now())return t.value}let t;try{const i=await this.fetchFn(`${this.baseUrl}/registry/resolve/${encodeURIComponent(e)}`);if(404===i.status)t=err("NOT_FOUND");else if(408===i.status)t=err("EXPIRED");else if(410===i.status)t=err("REVOKED");else if(i.ok){const e=await i.json();t=ok(new Uint8Array(e.publicKey))}else t=err("NETWORK_ERROR")}catch{const i=this.resolveCache.get(e);if("fail-secure"===this.cacheFailureMode)t=err("NETWORK_ERROR");else{if(i)return i.value;t=err("NETWORK_ERROR")}}return this.cacheTtlMs>0&&this.resolveCache.set(e,{value:t,expiry:Date.now()+this.cacheTtlMs}),t}async hasScope(e,t){try{return(await this.fetchFn(`${this.baseUrl}/registry/scope/${encodeURIComponent(e)}/${encodeURIComponent(t)}`)).ok}catch{return!1}}async hasReceiveScope(e,t){try{return(await this.fetchFn(`${this.baseUrl}/registry/receive-scope/${encodeURIComponent(e)}/${encodeURIComponent(t)}`)).ok}catch{return!1}}async revoke(e){try{const t=await this.fetchFn(`${this.baseUrl}/registry/revoke/${encodeURIComponent(e)}`,{method:"POST"});return 404===t.status?err("NOT_FOUND"):t.ok?ok(void 0):err("NETWORK_ERROR")}catch{return err("NETWORK_ERROR")}}async getEntry(e){if(this.cacheTtlMs>0){const t=this.entryCache.get(e);if(t&&t.expiry>Date.now())return t.value}let t;try{const i=await this.fetchFn(`${this.baseUrl}/registry/entry/${encodeURIComponent(e)}`);if(404===i.status)t=err("NOT_FOUND");else if(408===i.status)t=err("EXPIRED");else if(i.ok){const e=await i.json();t=ok({did:e.did,publicKey:new Uint8Array(e.publicKey),name:e.name,scopes:new Set(e.scopes),receiveScopes:e.receiveScopes?new Set(e.receiveScopes):void 0,revoked:e.revoked,rotation_sequence:e.rotation_sequence??1,x25519PublicKey:e.x25519PublicKey?new Uint8Array(e.x25519PublicKey):void 0,mlKemPublicKey:e.mlKemPublicKey?new Uint8Array(e.mlKemPublicKey):void 0,mlDsaPublicKey:e.mlDsaPublicKey?new Uint8Array(e.mlDsaPublicKey):void 0,xchange:e.xchange,sdkVersion:e.sdkVersion,minEnvelopeVersion:e.minEnvelopeVersion,maxEnvelopeVersion:e.maxEnvelopeVersion,expiresAt:e.expiresAt})}else t=err("NETWORK_ERROR")}catch{t=err("NETWORK_ERROR")}return this.cacheTtlMs>0&&this.entryCache.set(e,{value:t,expiry:Date.now()+this.cacheTtlMs}),t}async rotate(e,t,i,s){const r=await this.fetchFn(`${this.baseUrl}/registry/rotate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({did:e,newPublicKey:Array.from(t),proof:Array.from(i),rotationSequence:s})});if(!r.ok)throw new Error(`Key rotation failed: ${r.status} ${r.statusText}`);this.resolveCache.delete(e),this.entryCache.delete(e)}async subscribe(e,t){if(!this.enablePush)throw new Error("Push notifications not enabled (set enablePush: true in HttpTrustRegistryOptions)");const i=new Set(e.map(e=>this.hashDid(e))),s=this.baseUrl.replace(/^http/,"ws")+"/trust/events",r=new globalThis.WebSocket(s);return r.addEventListener("open",()=>{r.send(JSON.stringify({type:"subscribe",dids:e,bloomSize:this.bloomFilterSize,bloomFpr:this.bloomFilterFpr}))}),r.addEventListener("message",e=>{try{const s=JSON.parse(e.data),r=this.hashDid(s.did);i.has(r)&&("revocation"!==s.type&&"succession"!==s.type||(this.resolveCache.delete(s.did),this.entryCache.delete(s.did)),t(s))}catch(e){console.warn("Failed to parse trust event:",e)}}),r.addEventListener("error",e=>{console.error("WebSocket error:",e)}),()=>{r.readyState===globalThis.WebSocket.OPEN&&r.close()}}hashDid(e){let t=0;for(let i=0;i<e.length;i++)t=(t<<5)-t+e.charCodeAt(i),t&=t;return t}async resumeSubscriptions(e){try{return(await this.fetchFn(`${this.baseUrl}/trust/resume-batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({proofs:e})})).ok?ok(void 0):err("NETWORK_ERROR")}catch{return err("NETWORK_ERROR")}}async fetchCheckpoint(e){try{const t=await this.fetchFn(`${this.baseUrl}/registry/checkpoint/${encodeURIComponent(e)}`);if(404===t.status)return err("NOT_FOUND");if(!t.ok)return err("NETWORK_ERROR");const i=await t.json();return ok(i)}catch{return err("NETWORK_ERROR")}}async updateScopes(e,t){try{const i=await this.fetchFn(`${this.baseUrl}/registry/${encodeURIComponent(e)}/scopes`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({scopes:t})});return 404===i.status?err("NOT_FOUND"):410===i.status?err("REVOKED"):i.ok?(this.resolveCache.delete(e),this.entryCache.delete(e),ok(void 0)):err("NETWORK_ERROR")}catch{return err("NETWORK_ERROR")}}}export class FileTrustRegistry{path;entries=new Map;initialized=!1;constructor(e){this.path=e.path}async init(){if(!this.initialized){try{await fs.mkdir(path.dirname(this.path),{recursive:!0});const e=(await fs.readFile(this.path,"utf-8").catch(()=>"")).split("\n").filter(e=>e.trim());for(const t of e){const e=JSON.parse(t);if("register"===e.type&&e.publicKey&&e.name)this.entries.set(e.did,{did:e.did,publicKey:new Uint8Array(e.publicKey),name:e.name,scopes:new Set(e.scopes??[]),receiveScopes:e.receiveScopes?new Set(e.receiveScopes):void 0,revoked:!1,rotation_sequence:e.rotation_sequence??1,x25519PublicKey:e.x25519PublicKey?new Uint8Array(e.x25519PublicKey):void 0,mlKemPublicKey:e.mlKemPublicKey?new Uint8Array(e.mlKemPublicKey):void 0,mlDsaPublicKey:e.mlDsaPublicKey?new Uint8Array(e.mlDsaPublicKey):void 0,xchange:e.xchange,sdkVersion:e.sdkVersion,minEnvelopeVersion:e.minEnvelopeVersion,maxEnvelopeVersion:e.maxEnvelopeVersion,expiresAt:e.expiresAt});else if("revoke"===e.type){const t=this.entries.get(e.did);t&&this.entries.set(e.did,{...t,revoked:!0})}else if("update-scopes"===e.type){const t=this.entries.get(e.did);t&&this.entries.set(e.did,{...t,scopes:new Set(e.scopes??[])})}else if("rotate"===e.type&&e.publicKey&&e.rotation_sequence){const t=this.entries.get(e.did);t&&e.rotation_sequence>t.rotation_sequence&&this.entries.set(e.did,{...t,publicKey:new Uint8Array(e.publicKey),rotation_sequence:e.rotation_sequence})}}}catch(e){}this.initialized=!0}}async append(e){await fs.appendFile(this.path,JSON.stringify(e)+"\n","utf-8")}async register(e,t,i,s,r,n,o,a,c,l,h,p,u,d){if(await this.init(),this.entries.has(e))return err("ALREADY_REGISTERED");const y=u?Date.now()+u:void 0,m={did:e,publicKey:t,name:i,scopes:new Set(s??[]),receiveScopes:c?new Set(c):void 0,revoked:!1,rotation_sequence:1,x25519PublicKey:r,mlKemPublicKey:n,mlDsaPublicKey:o,xchange:a,sdkVersion:l,minEnvelopeVersion:h,maxEnvelopeVersion:p,expiresAt:y};return this.entries.set(e,m),await this.append({type:"register",did:e,publicKey:Array.from(t),name:i,scopes:s??[],rotation_sequence:1,...c?{receiveScopes:c}:{},...r?{x25519PublicKey:Array.from(r)}:{},...n?{mlKemPublicKey:Array.from(n)}:{},...o?{mlDsaPublicKey:Array.from(o)}:{},...void 0!==a?{xchange:a}:{},...void 0!==l?{sdkVersion:l}:{},...void 0!==h?{minEnvelopeVersion:h}:{},...void 0!==p?{maxEnvelopeVersion:p}:{},...void 0!==y?{expiresAt:y}:{}}),ok(void 0)}async resolve(e){await this.init();const t=this.entries.get(e);return t?isExpired(t)?err("EXPIRED"):t.revoked?err("REVOKED"):ok(t.publicKey):err("NOT_FOUND")}async hasScope(e,t){await this.init();const i=this.entries.get(e);return!(!i||isExpired(i)||i.revoked)&&i.scopes.has(t)}async hasReceiveScope(e,t){await this.init();const i=this.entries.get(e);return!(!i||isExpired(i)||i.revoked)&&(!i.receiveScopes||i.receiveScopes.has(t))}async revoke(e){await this.init();const t=this.entries.get(e);return t?(this.entries.set(e,{...t,revoked:!0}),await this.append({type:"revoke",did:e}),ok(void 0)):err("NOT_FOUND")}async getEntry(e){await this.init();const t=this.entries.get(e);return t?isExpired(t)?err("EXPIRED"):ok(t):err("NOT_FOUND")}async updateScopes(e,t){await this.init();const i=this.entries.get(e);return i?isExpired(i)?err("EXPIRED"):i.revoked?err("REVOKED"):(this.entries.set(e,{...i,scopes:new Set(t)}),await this.append({type:"update-scopes",did:e,scopes:t}),ok(void 0)):err("NOT_FOUND")}async cleanup(){await this.init();let e=0;const t=Date.now();for(const[i,s]of this.entries)s.expiresAt&&t>s.expiresAt&&(this.entries.delete(i),e++);return e}async rotate(e,t,i,s){await this.init();const r=this.entries.get(e);if(!r)throw new Error(`DID not found: ${e}`);if(s<=r.rotation_sequence)throw new Error(`Rotation sequence ${s} must be > current ${r.rotation_sequence} (rollback attack prevented)`);await this.append({type:"rotate",did:e,publicKey:Array.from(t),proof:Array.from(i),rotation_sequence:s,timestamp:Date.now()}),this.entries.set(e,{...r,publicKey:t,rotation_sequence:s})}get size(){return this.entries.size}}export async function createEnterpriseTrustRegistry(e){let t;if("file"===e.storage){if(!e.path)throw new Error("FileTrustRegistry requires path option");t=new FileTrustRegistry({path:e.path})}else if("http"===e.storage){if(!e.baseUrl)throw new Error("HttpTrustRegistry requires baseUrl option");t=new HttpTrustRegistry({baseUrl:e.baseUrl})}else t=new MemoryTrustRegistry;if(e.preload)for(const i of e.preload)await t.register(i.did,i.publicKey,i.name,i.scopes,i.x25519PublicKey,i.mlKemPublicKey,i.mlDsaPublicKey,i.xchange,i.receiveScopes,i.sdkVersion,i.minEnvelopeVersion,i.maxEnvelopeVersion);return t}
|