@enbox/agent 0.3.1 → 0.5.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/dist/browser.mjs +12 -30
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/dwn-api.js +149 -22
- package/dist/esm/dwn-api.js.map +1 -1
- package/dist/esm/dwn-discovery-file.js +1 -1
- package/dist/esm/dwn-discovery-payload.js +20 -21
- package/dist/esm/dwn-discovery-payload.js.map +1 -1
- package/dist/esm/dwn-key-delivery.js.map +1 -1
- package/dist/esm/{oidc.js → enbox-connect-protocol.js} +219 -251
- package/dist/esm/enbox-connect-protocol.js.map +1 -0
- package/dist/esm/enbox-user-agent.js +19 -12
- package/dist/esm/enbox-user-agent.js.map +1 -1
- package/dist/esm/hd-identity-vault.js +11 -0
- package/dist/esm/hd-identity-vault.js.map +1 -1
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/local-dwn.js +21 -51
- package/dist/esm/local-dwn.js.map +1 -1
- package/dist/esm/permissions-api.js.map +1 -1
- package/dist/esm/store-data.js.map +1 -1
- package/dist/esm/sync-engine-level.js +1 -1
- package/dist/esm/sync-engine-level.js.map +1 -1
- package/dist/esm/sync-messages.js +1 -1
- package/dist/esm/sync-messages.js.map +1 -1
- package/dist/esm/test-harness.js +2 -3
- package/dist/esm/test-harness.js.map +1 -1
- package/dist/esm/types/dwn.js.map +1 -1
- package/dist/types/dwn-api.d.ts +46 -6
- package/dist/types/dwn-api.d.ts.map +1 -1
- package/dist/types/dwn-discovery-file.d.ts +1 -1
- package/dist/types/dwn-discovery-payload.d.ts +18 -19
- package/dist/types/dwn-discovery-payload.d.ts.map +1 -1
- package/dist/types/enbox-connect-protocol.d.ts +206 -0
- package/dist/types/enbox-connect-protocol.d.ts.map +1 -0
- package/dist/types/enbox-user-agent.d.ts +13 -8
- package/dist/types/enbox-user-agent.d.ts.map +1 -1
- package/dist/types/hd-identity-vault.d.ts +7 -0
- package/dist/types/hd-identity-vault.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/local-dwn.d.ts +16 -32
- package/dist/types/local-dwn.d.ts.map +1 -1
- package/dist/types/test-harness.d.ts.map +1 -1
- package/dist/types/types/agent.d.ts +2 -7
- package/dist/types/types/agent.d.ts.map +1 -1
- package/dist/types/types/dwn.d.ts +0 -10
- package/dist/types/types/dwn.d.ts.map +1 -1
- package/dist/types/types/sync.d.ts +6 -0
- package/dist/types/types/sync.d.ts.map +1 -1
- package/package.json +14 -16
- package/src/dwn-api.ts +175 -29
- package/src/dwn-discovery-file.ts +1 -1
- package/src/dwn-discovery-payload.ts +23 -24
- package/src/dwn-key-delivery.ts +1 -1
- package/src/enbox-connect-protocol.ts +753 -0
- package/src/enbox-user-agent.ts +31 -18
- package/src/hd-identity-vault.ts +21 -0
- package/src/index.ts +4 -4
- package/src/local-dwn.ts +22 -53
- package/src/permissions-api.ts +3 -3
- package/src/store-data.ts +1 -1
- package/src/sync-engine-level.ts +1 -1
- package/src/sync-messages.ts +1 -1
- package/src/test-harness.ts +2 -3
- package/src/types/agent.ts +3 -14
- package/src/types/dwn.ts +1 -13
- package/src/types/sync.ts +7 -0
- package/dist/esm/connect.js +0 -180
- package/dist/esm/connect.js.map +0 -1
- package/dist/esm/oidc.js.map +0 -1
- package/dist/esm/sync-api.js +0 -64
- package/dist/esm/sync-api.js.map +0 -1
- package/dist/types/connect.d.ts +0 -88
- package/dist/types/connect.d.ts.map +0 -1
- package/dist/types/oidc.d.ts +0 -250
- package/dist/types/oidc.d.ts.map +0 -1
- package/dist/types/sync-api.d.ts +0 -40
- package/dist/types/sync-api.d.ts.map +0 -1
- package/src/connect.ts +0 -285
- package/src/oidc.ts +0 -864
- package/src/sync-api.ts +0 -75
package/src/enbox-user-agent.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { BearerDid } from '@enbox/dids';
|
|
|
3
3
|
import type { EnboxPlatformAgent } from './types/agent.js';
|
|
4
4
|
import type { EnboxRpc } from '@enbox/dwn-clients';
|
|
5
5
|
import type { LocalDwnStrategy } from './local-dwn.js';
|
|
6
|
+
import type { SyncEngine } from './types/sync.js';
|
|
6
7
|
import type { DidInterface, DidRequest, DidResponse } from './did-api.js';
|
|
7
8
|
import type { DwnInterface, DwnResponse, ProcessDwnRequest, SendDwnRequest } from './types/dwn.js';
|
|
8
9
|
import type { ProcessVcRequest, SendVcRequest, VcResponse } from './types/vc.js';
|
|
@@ -13,7 +14,6 @@ import { AgentDidResolverCache } from './agent-did-resolver-cache.js';
|
|
|
13
14
|
import { AgentDwnApi } from './dwn-api.js';
|
|
14
15
|
import { AgentIdentityApi } from './identity-api.js';
|
|
15
16
|
import { AgentPermissionsApi } from './permissions-api.js';
|
|
16
|
-
import { AgentSyncApi } from './sync-api.js';
|
|
17
17
|
import { DwnDidStore } from './store-did.js';
|
|
18
18
|
import { DwnIdentityStore } from './store-identity.js';
|
|
19
19
|
import { DwnKeyStore } from './store-key.js';
|
|
@@ -85,11 +85,21 @@ export type AgentParams<TKeyManager extends AgentKeyManager = LocalKeyManager> =
|
|
|
85
85
|
/** Remote procedure call (RPC) client used to communicate with other Enbox services. */
|
|
86
86
|
rpcClient: EnboxRpc;
|
|
87
87
|
/** Facilitates data synchronization of DWN records between nodes. */
|
|
88
|
-
syncApi:
|
|
88
|
+
syncApi: SyncEngine;
|
|
89
89
|
};
|
|
90
90
|
|
|
91
91
|
export type CreateUserAgentParams = Partial<AgentParams> & {
|
|
92
92
|
localDwnStrategy?: LocalDwnStrategy;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* When set, the agent operates in "remote mode": no in-process DWN is
|
|
96
|
+
* created. All `processRequest()` calls are routed through RPC to
|
|
97
|
+
* this endpoint instead.
|
|
98
|
+
*
|
|
99
|
+
* Typically set by `AuthManager.create()` after standalone discovery
|
|
100
|
+
* determines that a local DWN server is running.
|
|
101
|
+
*/
|
|
102
|
+
localDwnEndpoint?: string;
|
|
93
103
|
};
|
|
94
104
|
|
|
95
105
|
export class EnboxUserAgent<TKeyManager extends AgentKeyManager = LocalKeyManager> implements EnboxPlatformAgent<TKeyManager> {
|
|
@@ -100,7 +110,7 @@ export class EnboxUserAgent<TKeyManager extends AgentKeyManager = LocalKeyManage
|
|
|
100
110
|
public keyManager: TKeyManager;
|
|
101
111
|
public permissions: AgentPermissionsApi;
|
|
102
112
|
public rpc: EnboxRpc;
|
|
103
|
-
public sync:
|
|
113
|
+
public sync: SyncEngine;
|
|
104
114
|
public vault: HdIdentityVault;
|
|
105
115
|
|
|
106
116
|
private _agentDid?: BearerDid;
|
|
@@ -146,6 +156,7 @@ export class EnboxUserAgent<TKeyManager extends AgentKeyManager = LocalKeyManage
|
|
|
146
156
|
public static async create({
|
|
147
157
|
dataPath = 'DATA/AGENT',
|
|
148
158
|
localDwnStrategy,
|
|
159
|
+
localDwnEndpoint,
|
|
149
160
|
agentDid, agentVault, cryptoApi, didApi, dwnApi, identityApi, keyManager, permissionsApi, rpcClient, syncApi
|
|
150
161
|
}: CreateUserAgentParams = {}
|
|
151
162
|
): Promise<EnboxUserAgent> {
|
|
@@ -163,10 +174,22 @@ export class EnboxUserAgent<TKeyManager extends AgentKeyManager = LocalKeyManage
|
|
|
163
174
|
store : new DwnDidStore()
|
|
164
175
|
});
|
|
165
176
|
|
|
166
|
-
dwnApi
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
177
|
+
if (!dwnApi) {
|
|
178
|
+
if (localDwnEndpoint) {
|
|
179
|
+
// Remote mode: no in-process DWN. All operations route through
|
|
180
|
+
// RPC to the local DWN server.
|
|
181
|
+
dwnApi = new AgentDwnApi({
|
|
182
|
+
localDwnEndpoint,
|
|
183
|
+
localDwnStrategy: localDwnStrategy ?? 'prefer',
|
|
184
|
+
});
|
|
185
|
+
} else {
|
|
186
|
+
// Local mode: create an in-process DWN with LevelDB stores.
|
|
187
|
+
dwnApi = new AgentDwnApi({
|
|
188
|
+
dwn : await AgentDwnApi.createDwn({ dataPath, didResolver: didApi }),
|
|
189
|
+
localDwnStrategy : localDwnStrategy ?? 'prefer',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
170
193
|
if (localDwnStrategy) {
|
|
171
194
|
dwnApi.setLocalDwnStrategy(localDwnStrategy);
|
|
172
195
|
}
|
|
@@ -179,7 +202,7 @@ export class EnboxUserAgent<TKeyManager extends AgentKeyManager = LocalKeyManage
|
|
|
179
202
|
|
|
180
203
|
rpcClient ??= new EnboxRpcClient();
|
|
181
204
|
|
|
182
|
-
syncApi ??= new
|
|
205
|
+
syncApi ??= new SyncEngineLevel({ dataPath });
|
|
183
206
|
|
|
184
207
|
// Instantiate the Agent using the provided or default components.
|
|
185
208
|
return new EnboxUserAgent({
|
|
@@ -261,13 +284,3 @@ export class EnboxUserAgent<TKeyManager extends AgentKeyManager = LocalKeyManage
|
|
|
261
284
|
this.agentDid = await this.vault.getDid();
|
|
262
285
|
}
|
|
263
286
|
}
|
|
264
|
-
|
|
265
|
-
// ---------------------------------------------------------------------------
|
|
266
|
-
// Deprecated aliases — migration aid
|
|
267
|
-
// ---------------------------------------------------------------------------
|
|
268
|
-
|
|
269
|
-
/** @deprecated Use {@link EnboxUserAgent} instead. Will be removed in a future version. */
|
|
270
|
-
export const Web5UserAgent = EnboxUserAgent;
|
|
271
|
-
|
|
272
|
-
/** @deprecated Use {@link EnboxUserAgent} instead. Will be removed in a future version. */
|
|
273
|
-
export type Web5UserAgent = EnboxUserAgent;
|
package/src/hd-identity-vault.ts
CHANGED
|
@@ -150,6 +150,14 @@ export class HdIdentityVault implements IdentityVault<{ InitializeResult: string
|
|
|
150
150
|
/** The cryptographic key used to encrypt and decrypt the vault's content securely. */
|
|
151
151
|
private _contentEncryptionKey: Jwk | undefined;
|
|
152
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Cached initialization state. Once read from the store, avoids redundant LevelDB reads on
|
|
155
|
+
* subsequent checks. The `initialized` flag is write-once (false → true) and never reverts,
|
|
156
|
+
* making it safe to cache indefinitely. Mirrors the pattern used by {@link _contentEncryptionKey}
|
|
157
|
+
* for the synchronous {@link isLocked} check.
|
|
158
|
+
*/
|
|
159
|
+
private _cachedInitialized: boolean | undefined;
|
|
160
|
+
|
|
153
161
|
/**
|
|
154
162
|
* Constructs an instance of `HdIdentityVault`, initializing the key derivation factor and data
|
|
155
163
|
* store. It sets the default key derivation work factor and initializes the internal data store,
|
|
@@ -337,6 +345,13 @@ export class HdIdentityVault implements IdentityVault<{ InitializeResult: string
|
|
|
337
345
|
throw new Error('HdIdentityVault: Invalid IdentityVaultStatus object in store');
|
|
338
346
|
}
|
|
339
347
|
|
|
348
|
+
// Only cache the `true` state — `initialized` is write-once (false → true) and never reverts,
|
|
349
|
+
// so a cached `true` is always valid. Leaving `false` uncached ensures a subsequent
|
|
350
|
+
// `isInitialized()` call after `initialize()` correctly reads the updated store.
|
|
351
|
+
if (vaultStatus.initialized) {
|
|
352
|
+
this._cachedInitialized = true;
|
|
353
|
+
}
|
|
354
|
+
|
|
340
355
|
return vaultStatus;
|
|
341
356
|
}
|
|
342
357
|
|
|
@@ -595,6 +610,9 @@ export class HdIdentityVault implements IdentityVault<{ InitializeResult: string
|
|
|
595
610
|
* @returns A promise that resolves to `true` if the vault has been initialized, otherwise `false`.
|
|
596
611
|
*/
|
|
597
612
|
public async isInitialized(): Promise<boolean> {
|
|
613
|
+
if (this._cachedInitialized === true) {
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
598
616
|
return this.getStatus().then(({ initialized }) => initialized);
|
|
599
617
|
}
|
|
600
618
|
|
|
@@ -851,6 +869,9 @@ export class HdIdentityVault implements IdentityVault<{ InitializeResult: string
|
|
|
851
869
|
// Write the changes to the store.
|
|
852
870
|
await this._store.set('vaultStatus', JSON.stringify(vaultStatus));
|
|
853
871
|
|
|
872
|
+
// Update the in-memory cache so subsequent reads skip the store.
|
|
873
|
+
this._cachedInitialized = vaultStatus.initialized;
|
|
874
|
+
|
|
854
875
|
return true;
|
|
855
876
|
}
|
|
856
877
|
}
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,9 @@ export * from './dwn-discovery-file.js';
|
|
|
17
17
|
export * from './dwn-discovery-payload.js';
|
|
18
18
|
export * from './dwn-encryption.js';
|
|
19
19
|
export * from './dwn-key-delivery.js';
|
|
20
|
-
|
|
20
|
+
// NOTE: dwn-record-upgrade.js is intentionally NOT exported — the module
|
|
21
|
+
// is disabled (see TODO in dwn-api.ts postWriteKeyDelivery). Keeping the
|
|
22
|
+
// source file for reference until the redesign in a future PR.
|
|
21
23
|
export * from './dwn-type-guards.js';
|
|
22
24
|
export * from './protocol-utils.js';
|
|
23
25
|
export * from './hd-identity-vault.js';
|
|
@@ -29,10 +31,8 @@ export * from './store-data.js';
|
|
|
29
31
|
export * from './store-did.js';
|
|
30
32
|
export * from './store-identity.js';
|
|
31
33
|
export * from './store-key.js';
|
|
32
|
-
export * from './sync-api.js';
|
|
33
34
|
export * from './sync-engine-level.js';
|
|
34
35
|
export * from './test-harness.js';
|
|
35
36
|
export * from './utils.js';
|
|
36
|
-
export * from './connect.js';
|
|
37
|
-
export * from './oidc.js';
|
|
37
|
+
export * from './enbox-connect-protocol.js';
|
|
38
38
|
export * from './enbox-user-agent.js';
|
package/src/local-dwn.ts
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* 2. **Discovery file** (`~/.enbox/dwn.json`) — written by `electrobun-dwn`
|
|
8
8
|
* on startup. Fast filesystem read, no network. Available for CLI and
|
|
9
9
|
* native apps; skipped in browsers.
|
|
10
|
-
* 3. **
|
|
11
|
-
*
|
|
10
|
+
* 3. **Injected endpoint** — in browsers, the `dwn://connect` redirect
|
|
11
|
+
* flow delivers the endpoint, which is injected via
|
|
12
|
+
* {@link LocalDwnDiscovery.setCachedEndpoint | setCachedEndpoint()}.
|
|
12
13
|
*
|
|
13
|
-
* @see https://github.com/enboxorg/enbox/issues/
|
|
14
|
+
* @see https://github.com/enboxorg/enbox/issues/677
|
|
14
15
|
* @module
|
|
15
16
|
*/
|
|
16
17
|
|
|
@@ -18,31 +19,11 @@ import type { EnboxRpc } from '@enbox/dwn-clients';
|
|
|
18
19
|
|
|
19
20
|
import type { DwnDiscoveryFile } from './dwn-discovery-file.js';
|
|
20
21
|
|
|
21
|
-
/**
|
|
22
|
-
* Well-known ports the local DWN desktop app may bind to.
|
|
23
|
-
*
|
|
24
|
-
* Per the DWN Transport Spec, clients probe ports `55500` through `55509`
|
|
25
|
-
* (inclusive). Port `3000` is included as a development convenience.
|
|
26
|
-
*
|
|
27
|
-
* @see https://identity.foundation/dwn-transport/#port-probing
|
|
28
|
-
*/
|
|
29
|
-
export const localDwnPortCandidates = [3000, 55500, 55501, 55502, 55503, 55504, 55505, 55506, 55507, 55508, 55509] as const;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Hosts probed when discovering a local DWN server.
|
|
33
|
-
*
|
|
34
|
-
* Per the DWN Transport Spec, clients MUST use `127.0.0.1` rather than
|
|
35
|
-
* `localhost` to avoid DNS resolution ambiguity.
|
|
36
|
-
*
|
|
37
|
-
* @see https://identity.foundation/dwn-transport/#port-probing
|
|
38
|
-
*/
|
|
39
|
-
export const localDwnHostCandidates = ['127.0.0.1'] as const;
|
|
40
|
-
|
|
41
22
|
/**
|
|
42
23
|
* Controls how the agent discovers and routes to a local DWN server.
|
|
43
24
|
*
|
|
44
25
|
* - `'off'` — (default) skip local discovery entirely.
|
|
45
|
-
* - `'prefer'` —
|
|
26
|
+
* - `'prefer'` — try local DWN first; fall back to DID-document endpoints.
|
|
46
27
|
* - `'only'` — require a local server; throw if none is found.
|
|
47
28
|
*/
|
|
48
29
|
export type LocalDwnStrategy = 'prefer' | 'only' | 'off';
|
|
@@ -61,7 +42,7 @@ export function normalizeBaseUrl(url: string): string {
|
|
|
61
42
|
* Results are cached for {@link _cacheTtlMs} milliseconds (default 10 s) to
|
|
62
43
|
* avoid repeated I/O on hot paths such as sync.
|
|
63
44
|
*
|
|
64
|
-
* @example Discovery with file-based channel
|
|
45
|
+
* @example Discovery with file-based channel (CLI / native)
|
|
65
46
|
* ```ts
|
|
66
47
|
* import { DwnDiscoveryFile } from './dwn-discovery-file.js';
|
|
67
48
|
*
|
|
@@ -70,7 +51,7 @@ export function normalizeBaseUrl(url: string): string {
|
|
|
70
51
|
* const endpoint = await discovery.getEndpoint();
|
|
71
52
|
* ```
|
|
72
53
|
*
|
|
73
|
-
* @example Browser: inject cached endpoint from `dwn://
|
|
54
|
+
* @example Browser: inject cached endpoint from `dwn://connect` redirect
|
|
74
55
|
* ```ts
|
|
75
56
|
* const discovery = new LocalDwnDiscovery(rpcClient);
|
|
76
57
|
* discovery.setCachedEndpoint('http://127.0.0.1:55557');
|
|
@@ -95,7 +76,14 @@ export class LocalDwnDiscovery {
|
|
|
95
76
|
* 2. `~/.enbox/dwn.json` discovery file (if a {@link DwnDiscoveryFile}
|
|
96
77
|
* was provided). The endpoint from the file is validated via
|
|
97
78
|
* `GET /info` to ensure the server is still running.
|
|
98
|
-
*
|
|
79
|
+
*
|
|
80
|
+
* If neither channel finds an endpoint, the result (`undefined`) is
|
|
81
|
+
* cached to avoid repeated discovery file reads on hot paths.
|
|
82
|
+
*
|
|
83
|
+
* In browser environments (where no discovery file is available), the
|
|
84
|
+
* endpoint must be injected externally via
|
|
85
|
+
* {@link setCachedEndpoint | setCachedEndpoint()} — typically after a
|
|
86
|
+
* `dwn://connect` redirect delivers the endpoint in the URL fragment.
|
|
99
87
|
*/
|
|
100
88
|
public async getEndpoint(): Promise<string | undefined> {
|
|
101
89
|
const now = Date.now();
|
|
@@ -103,22 +91,21 @@ export class LocalDwnDiscovery {
|
|
|
103
91
|
return this._cachedEndpoint;
|
|
104
92
|
}
|
|
105
93
|
|
|
106
|
-
//
|
|
94
|
+
// File-based discovery (CLI / native — skipped when no file is configured).
|
|
107
95
|
const fileEndpoint = await this._tryDiscoveryFile();
|
|
108
96
|
if (fileEndpoint !== undefined) {
|
|
109
97
|
this._setCacheEntry(fileEndpoint, now);
|
|
110
98
|
return fileEndpoint;
|
|
111
99
|
}
|
|
112
100
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return probeEndpoint;
|
|
101
|
+
// No endpoint found. Cache the negative result to avoid repeated
|
|
102
|
+
// discovery file reads within the TTL window.
|
|
103
|
+
this._setCacheEntry(undefined, now);
|
|
104
|
+
return undefined;
|
|
118
105
|
}
|
|
119
106
|
|
|
120
107
|
/**
|
|
121
|
-
* Inject a cached endpoint (e.g. from a `dwn://
|
|
108
|
+
* Inject a cached endpoint (e.g. from a `dwn://connect` browser redirect
|
|
122
109
|
* or from `localStorage`). The endpoint is validated via `GET /info` before
|
|
123
110
|
* caching.
|
|
124
111
|
*
|
|
@@ -142,7 +129,7 @@ export class LocalDwnDiscovery {
|
|
|
142
129
|
this._cacheExpiry = 0;
|
|
143
130
|
}
|
|
144
131
|
|
|
145
|
-
// ─── Private
|
|
132
|
+
// ─── Private ──────────────────────────────────────────────────
|
|
146
133
|
|
|
147
134
|
/**
|
|
148
135
|
* Try the `~/.enbox/dwn.json` discovery file. Returns the endpoint if
|
|
@@ -168,24 +155,6 @@ export class LocalDwnDiscovery {
|
|
|
168
155
|
}
|
|
169
156
|
}
|
|
170
157
|
|
|
171
|
-
/**
|
|
172
|
-
* Sequential HTTP probe on well-known localhost port candidates.
|
|
173
|
-
* Returns the first endpoint whose `GET /info` response identifies
|
|
174
|
-
* as `@enbox/dwn-server`, or `undefined` if none is found.
|
|
175
|
-
*/
|
|
176
|
-
private async _probePortCandidates(): Promise<string | undefined> {
|
|
177
|
-
for (const port of localDwnPortCandidates) {
|
|
178
|
-
for (const host of localDwnHostCandidates) {
|
|
179
|
-
const endpoint = `http://${host}:${port}`;
|
|
180
|
-
const valid = await this._validateEndpoint(endpoint);
|
|
181
|
-
if (valid) {
|
|
182
|
-
return normalizeBaseUrl(endpoint);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return undefined;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
158
|
/**
|
|
190
159
|
* Call `GET /info` on the endpoint and check that
|
|
191
160
|
* `serverInfo.server === '@enbox/dwn-server'`.
|
package/src/permissions-api.ts
CHANGED
|
@@ -263,7 +263,7 @@ export class AgentPermissionsApi implements PermissionsApi {
|
|
|
263
263
|
target : author,
|
|
264
264
|
messageType : DwnInterface.RecordsWrite,
|
|
265
265
|
messageParams,
|
|
266
|
-
dataStream : new Blob([ permissionsGrantBytes ])
|
|
266
|
+
dataStream : new Blob([ permissionsGrantBytes as BlobPart ])
|
|
267
267
|
});
|
|
268
268
|
|
|
269
269
|
if (reply.status.code !== 202) {
|
|
@@ -309,7 +309,7 @@ export class AgentPermissionsApi implements PermissionsApi {
|
|
|
309
309
|
target : author,
|
|
310
310
|
messageType : DwnInterface.RecordsWrite,
|
|
311
311
|
messageParams,
|
|
312
|
-
dataStream : new Blob([ permissionRequestBytes ])
|
|
312
|
+
dataStream : new Blob([ permissionRequestBytes as BlobPart ])
|
|
313
313
|
});
|
|
314
314
|
|
|
315
315
|
if (reply.status.code !== 202) {
|
|
@@ -352,7 +352,7 @@ export class AgentPermissionsApi implements PermissionsApi {
|
|
|
352
352
|
target : author,
|
|
353
353
|
messageType : DwnInterface.RecordsWrite,
|
|
354
354
|
messageParams,
|
|
355
|
-
dataStream : new Blob([ permissionRevocationBytes ])
|
|
355
|
+
dataStream : new Blob([ permissionRevocationBytes as BlobPart ])
|
|
356
356
|
});
|
|
357
357
|
|
|
358
358
|
if (reply.status.code !== 202) {
|
package/src/store-data.ts
CHANGED
|
@@ -194,7 +194,7 @@ export class DwnDataStore<TStoreObject extends Record<string, any> = Jwk> implem
|
|
|
194
194
|
target : tenantDid,
|
|
195
195
|
messageType : DwnInterface.RecordsWrite,
|
|
196
196
|
messageParams : { ...this._recordProperties, ...messageParams },
|
|
197
|
-
dataStream : new Blob([dataBytes], { type: 'application/json' }),
|
|
197
|
+
dataStream : new Blob([dataBytes as BlobPart], { type: 'application/json' }),
|
|
198
198
|
...(encryptionActive ? { encryption: true } : {}),
|
|
199
199
|
});
|
|
200
200
|
|
package/src/sync-engine-level.ts
CHANGED
|
@@ -521,7 +521,7 @@ export class SyncEngineLevel implements SyncEngine {
|
|
|
521
521
|
try {
|
|
522
522
|
// Process the message locally.
|
|
523
523
|
const dataStream = this.extractDataStream(event);
|
|
524
|
-
await this.agent.dwn.
|
|
524
|
+
await this.agent.dwn.processRawMessage(did, event.message, { dataStream });
|
|
525
525
|
} catch (error: any) {
|
|
526
526
|
console.error(`SyncEngineLevel: Error processing live-pull event for ${did}`, error);
|
|
527
527
|
}
|
package/src/sync-messages.ts
CHANGED
|
@@ -75,7 +75,7 @@ export async function pullMessages({ did, dwnUrl, delegateDid, protocol, message
|
|
|
75
75
|
const failedCids: string[] = [];
|
|
76
76
|
|
|
77
77
|
for (const entry of pending) {
|
|
78
|
-
const pullReply = await agent.dwn.
|
|
78
|
+
const pullReply = await agent.dwn.processRawMessage(did, entry.message, { dataStream: entry.dataStream });
|
|
79
79
|
if (!syncMessageReplyIsSuccessful(pullReply)) {
|
|
80
80
|
const cid = await getMessageCid(entry.message);
|
|
81
81
|
failedCids.push(cid);
|
package/src/test-harness.ts
CHANGED
|
@@ -16,7 +16,6 @@ import { AgentDidResolverCache } from './agent-did-resolver-cache.js';
|
|
|
16
16
|
import { AgentDwnApi } from './dwn-api.js';
|
|
17
17
|
import { AgentIdentityApi } from './identity-api.js';
|
|
18
18
|
import { AgentPermissionsApi } from './permissions-api.js';
|
|
19
|
-
import { AgentSyncApi } from './sync-api.js';
|
|
20
19
|
import { EnboxRpcClient } from '@enbox/dwn-clients';
|
|
21
20
|
import { HdIdentityVault } from './hd-identity-vault.js';
|
|
22
21
|
import { LocalKeyManager } from './local-key-manager.js';
|
|
@@ -107,6 +106,7 @@ export class PlatformAgentTestHarness {
|
|
|
107
106
|
await this.dwnResumableTaskStore.clear();
|
|
108
107
|
await this.syncStore.clear();
|
|
109
108
|
await this.vaultStore.clear();
|
|
109
|
+
(this.agent.vault as any)['_cachedInitialized'] = undefined;
|
|
110
110
|
await this.agent.permissions.clear();
|
|
111
111
|
this.dwnStores.clear();
|
|
112
112
|
|
|
@@ -282,8 +282,7 @@ export class PlatformAgentTestHarness {
|
|
|
282
282
|
|
|
283
283
|
// Instantiate Agent's Sync API using a custom LevelDB-backed store.
|
|
284
284
|
const syncStore = new Level(testDataPath('SYNC_STORE'));
|
|
285
|
-
const
|
|
286
|
-
const syncApi = new AgentSyncApi({ syncEngine });
|
|
285
|
+
const syncApi = new SyncEngineLevel({ db: syncStore });
|
|
287
286
|
|
|
288
287
|
// Create EnboxPlatformAgent instance
|
|
289
288
|
const agent = new agentClass({
|
package/src/types/agent.ts
CHANGED
|
@@ -5,10 +5,9 @@ import type { AgentDwnApi } from '../dwn-api.js';
|
|
|
5
5
|
import type { AgentIdentityApi } from '../identity-api.js';
|
|
6
6
|
import type { AgentKeyManager } from './key-manager.js';
|
|
7
7
|
import type { AgentPermissionsApi } from '../permissions-api.js';
|
|
8
|
-
import type { AgentSyncApi } from '../sync-api.js';
|
|
9
8
|
import type { EnboxRpc } from '@enbox/dwn-clients';
|
|
10
9
|
import type { IdentityVault } from './identity-vault.js';
|
|
11
|
-
import type {
|
|
10
|
+
import type { SyncEngine } from './sync.js';
|
|
12
11
|
import type { AgentDidApi, DidInterface, DidRequest, DidResponse } from '../did-api.js';
|
|
13
12
|
import type { DwnInterface, DwnResponse, ProcessDwnRequest, SendDwnRequest } from './dwn.js';
|
|
14
13
|
import type { ProcessVcRequest, SendVcRequest, VcResponse } from './vc.js';
|
|
@@ -168,7 +167,7 @@ export interface EnboxPlatformAgent<TKeyManager extends AgentKeyManager = AgentK
|
|
|
168
167
|
* The synchronization API, responsible for managing the consistency and real-time update of the
|
|
169
168
|
* agent's data with the state of the distributed network.
|
|
170
169
|
*/
|
|
171
|
-
sync:
|
|
170
|
+
sync: SyncEngine;
|
|
172
171
|
|
|
173
172
|
/**
|
|
174
173
|
* An instance of {@link IdentityVault}, providing secure storage and management of an Enbox Agent's
|
|
@@ -193,14 +192,4 @@ export interface EnboxPlatformAgent<TKeyManager extends AgentKeyManager = AgentK
|
|
|
193
192
|
* normal operation and readiness to process requests.
|
|
194
193
|
*/
|
|
195
194
|
start(params: unknown): Promise<unknown>;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// ---------------------------------------------------------------------------
|
|
199
|
-
// Deprecated aliases — migration aid
|
|
200
|
-
// ---------------------------------------------------------------------------
|
|
201
|
-
|
|
202
|
-
/** @deprecated Use {@link EnboxAgent} instead. Will be removed in a future version. */
|
|
203
|
-
export type Web5Agent = EnboxAgent;
|
|
204
|
-
|
|
205
|
-
/** @deprecated Use {@link EnboxPlatformAgent} instead. Will be removed in a future version. */
|
|
206
|
-
export type Web5PlatformAgent<TKeyManager extends AgentKeyManager = LocalKeyManager> = EnboxPlatformAgent<TKeyManager>;
|
|
195
|
+
}
|
package/src/types/dwn.ts
CHANGED
|
@@ -72,19 +72,7 @@ import {
|
|
|
72
72
|
*
|
|
73
73
|
* @see {@link https://github.com/enboxorg/dwn-spec | Enbox DWN Specification}
|
|
74
74
|
*/
|
|
75
|
-
export interface DwnDidService extends DidService {
|
|
76
|
-
/**
|
|
77
|
-
* @deprecated Optional legacy field. Resolve encryption keys from the DID document's
|
|
78
|
-
* `keyAgreement` verification methods instead.
|
|
79
|
-
*/
|
|
80
|
-
enc?: string | string[];
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* @deprecated Optional legacy field. Resolve signing keys from the DID document's
|
|
84
|
-
* `authentication` or `assertionMethod` verification methods instead.
|
|
85
|
-
*/
|
|
86
|
-
sig?: string | string[];
|
|
87
|
-
}
|
|
75
|
+
export interface DwnDidService extends DidService {}
|
|
88
76
|
|
|
89
77
|
export enum DwnInterface {
|
|
90
78
|
MessagesRead = DwnInterfaceName.Messages + DwnMethodName.Read,
|
package/src/types/sync.ts
CHANGED
|
@@ -106,4 +106,11 @@ export interface SyncEngine {
|
|
|
106
106
|
* @throws {Error} if the sync operation fails to stop before the timeout.
|
|
107
107
|
*/
|
|
108
108
|
stopSync(timeout?: number): Promise<void>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Release all resources held by the sync engine (LevelDB handles, timers,
|
|
112
|
+
* WebSocket subscriptions). After calling `close()`, the engine should not
|
|
113
|
+
* be reused.
|
|
114
|
+
*/
|
|
115
|
+
close(): Promise<void>;
|
|
109
116
|
}
|
package/dist/esm/connect.js
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { CryptoUtils } from '@enbox/crypto';
|
|
11
|
-
import { DidJwk } from '@enbox/dids';
|
|
12
|
-
import { Oidc } from './oidc.js';
|
|
13
|
-
import { pollWithTtl } from './utils.js';
|
|
14
|
-
import { Convert, logger } from '@enbox/common';
|
|
15
|
-
import { DwnInterfaceName, DwnMethodName } from '@enbox/dwn-sdk-js';
|
|
16
|
-
/**
|
|
17
|
-
* Initiates the wallet connect process. Used when a client wants to obtain
|
|
18
|
-
* a did from a provider.
|
|
19
|
-
*/
|
|
20
|
-
function initClient(_a) {
|
|
21
|
-
return __awaiter(this, arguments, void 0, function* ({ displayName, connectServerUrl, walletUri, permissionRequests, onWalletUriReady, validatePin, }) {
|
|
22
|
-
// ephemeral client did for ECDH, signing, verification
|
|
23
|
-
const clientDid = yield DidJwk.create();
|
|
24
|
-
// TODO: properly implement PKCE. this implementation is lacking server side validations and more.
|
|
25
|
-
// https://github.com/enboxorg/enbox/issues/829
|
|
26
|
-
// Derive the code challenge based on the code verifier
|
|
27
|
-
// const { codeChallengeBytes, codeChallengeBase64Url } =
|
|
28
|
-
// await Oidc.generateCodeChallenge();
|
|
29
|
-
const encryptionKey = CryptoUtils.randomBytes(32);
|
|
30
|
-
// build callback URL to pass into the auth request
|
|
31
|
-
const callbackEndpoint = Oidc.buildOidcUrl({
|
|
32
|
-
baseURL: connectServerUrl,
|
|
33
|
-
endpoint: 'callback',
|
|
34
|
-
});
|
|
35
|
-
// build the PAR request
|
|
36
|
-
const request = yield Oidc.createAuthRequest({
|
|
37
|
-
client_id: clientDid.uri,
|
|
38
|
-
scope: 'openid did:jwk',
|
|
39
|
-
redirect_uri: callbackEndpoint,
|
|
40
|
-
// custom properties:
|
|
41
|
-
// code_challenge : codeChallengeBase64Url,
|
|
42
|
-
// code_challenge_method : 'S256',
|
|
43
|
-
permissionRequests: permissionRequests,
|
|
44
|
-
displayName,
|
|
45
|
-
});
|
|
46
|
-
// Sign the Request Object using the Client DID's signing key.
|
|
47
|
-
const requestJwt = yield Oidc.signJwt({
|
|
48
|
-
did: clientDid,
|
|
49
|
-
data: request,
|
|
50
|
-
});
|
|
51
|
-
if (!requestJwt) {
|
|
52
|
-
throw new Error('Unable to sign requestObject');
|
|
53
|
-
}
|
|
54
|
-
// Encrypt the Request Object JWT using the code challenge.
|
|
55
|
-
const requestObjectJwe = yield Oidc.encryptAuthRequest({
|
|
56
|
-
jwt: requestJwt,
|
|
57
|
-
encryptionKey,
|
|
58
|
-
});
|
|
59
|
-
const pushedAuthorizationRequestEndpoint = Oidc.buildOidcUrl({
|
|
60
|
-
baseURL: connectServerUrl,
|
|
61
|
-
endpoint: 'pushedAuthorizationRequest',
|
|
62
|
-
});
|
|
63
|
-
const parResponse = yield fetch(pushedAuthorizationRequestEndpoint, {
|
|
64
|
-
body: JSON.stringify({ request: requestObjectJwe }),
|
|
65
|
-
method: 'POST',
|
|
66
|
-
headers: {
|
|
67
|
-
'Content-Type': 'application/json',
|
|
68
|
-
},
|
|
69
|
-
signal: AbortSignal.timeout(30000),
|
|
70
|
-
});
|
|
71
|
-
if (!parResponse.ok) {
|
|
72
|
-
throw new Error(`${parResponse.status}: ${parResponse.statusText}`);
|
|
73
|
-
}
|
|
74
|
-
const parData = yield parResponse.json();
|
|
75
|
-
// a deeplink to a compatible wallet. if the wallet scans this link it should receive
|
|
76
|
-
// a route to its Connect provider flow and the params of where to fetch the auth request.
|
|
77
|
-
logger.log(`Wallet URI: ${walletUri}`);
|
|
78
|
-
const generatedWalletUri = new URL(walletUri);
|
|
79
|
-
generatedWalletUri.searchParams.set('request_uri', parData.request_uri);
|
|
80
|
-
generatedWalletUri.searchParams.set('encryption_key', Convert.uint8Array(encryptionKey).toBase64Url());
|
|
81
|
-
// call user's callback so they can send the URI to the wallet as they see fit
|
|
82
|
-
onWalletUriReady(generatedWalletUri.toString());
|
|
83
|
-
const tokenUrl = Oidc.buildOidcUrl({
|
|
84
|
-
baseURL: connectServerUrl,
|
|
85
|
-
endpoint: 'token',
|
|
86
|
-
tokenParam: request.state,
|
|
87
|
-
});
|
|
88
|
-
// subscribe to receiving a response from the wallet with default TTL. receive ciphertext of {@link EnboxConnectAuthResponse}
|
|
89
|
-
const authResponse = yield pollWithTtl(() => fetch(tokenUrl, { signal: AbortSignal.timeout(30000) }));
|
|
90
|
-
if (authResponse) {
|
|
91
|
-
const jwe = yield (authResponse === null || authResponse === void 0 ? void 0 : authResponse.text());
|
|
92
|
-
// get the pin from the user and use it as AAD to decrypt
|
|
93
|
-
const pin = yield validatePin();
|
|
94
|
-
const jwt = yield Oidc.decryptAuthResponse(clientDid, jwe, pin);
|
|
95
|
-
const verifiedAuthResponse = (yield Oidc.verifyJwt({
|
|
96
|
-
jwt,
|
|
97
|
-
}));
|
|
98
|
-
return {
|
|
99
|
-
delegateGrants: verifiedAuthResponse.delegateGrants,
|
|
100
|
-
delegatePortableDid: verifiedAuthResponse.delegatePortableDid,
|
|
101
|
-
connectedDid: verifiedAuthResponse.iss,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Creates a set of Dwn Permission Scopes to request for a given protocol.
|
|
108
|
-
*
|
|
109
|
-
* If no permissions are provided, the default is to request all relevant record permissions (write, read, delete, query, subscribe).
|
|
110
|
-
* 'configure' is not included by default, as this gives the application a lot of control over the protocol.
|
|
111
|
-
*/
|
|
112
|
-
function createPermissionRequestForProtocol({ definition, permissions }) {
|
|
113
|
-
const requests = [];
|
|
114
|
-
// Add the ability to query for the specific protocol
|
|
115
|
-
requests.push({
|
|
116
|
-
protocol: definition.protocol,
|
|
117
|
-
interface: DwnInterfaceName.Protocols,
|
|
118
|
-
method: DwnMethodName.Query,
|
|
119
|
-
});
|
|
120
|
-
// A Messages.Read grant is a unified scope that covers MessagesRead, MessagesSync, and MessagesSubscribe.
|
|
121
|
-
// This single grant enables sync and real-time subscriptions for the protocol.
|
|
122
|
-
requests.push({
|
|
123
|
-
protocol: definition.protocol,
|
|
124
|
-
interface: DwnInterfaceName.Messages,
|
|
125
|
-
method: DwnMethodName.Read,
|
|
126
|
-
});
|
|
127
|
-
// We also request any additional permissions the user has requested for this protocol
|
|
128
|
-
for (const permission of permissions) {
|
|
129
|
-
switch (permission) {
|
|
130
|
-
case 'write':
|
|
131
|
-
requests.push({
|
|
132
|
-
protocol: definition.protocol,
|
|
133
|
-
interface: DwnInterfaceName.Records,
|
|
134
|
-
method: DwnMethodName.Write,
|
|
135
|
-
});
|
|
136
|
-
break;
|
|
137
|
-
case 'read':
|
|
138
|
-
requests.push({
|
|
139
|
-
protocol: definition.protocol,
|
|
140
|
-
interface: DwnInterfaceName.Records,
|
|
141
|
-
method: DwnMethodName.Read,
|
|
142
|
-
});
|
|
143
|
-
break;
|
|
144
|
-
case 'delete':
|
|
145
|
-
requests.push({
|
|
146
|
-
protocol: definition.protocol,
|
|
147
|
-
interface: DwnInterfaceName.Records,
|
|
148
|
-
method: DwnMethodName.Delete,
|
|
149
|
-
});
|
|
150
|
-
break;
|
|
151
|
-
case 'query':
|
|
152
|
-
requests.push({
|
|
153
|
-
protocol: definition.protocol,
|
|
154
|
-
interface: DwnInterfaceName.Records,
|
|
155
|
-
method: DwnMethodName.Query,
|
|
156
|
-
});
|
|
157
|
-
break;
|
|
158
|
-
case 'subscribe':
|
|
159
|
-
requests.push({
|
|
160
|
-
protocol: definition.protocol,
|
|
161
|
-
interface: DwnInterfaceName.Records,
|
|
162
|
-
method: DwnMethodName.Subscribe,
|
|
163
|
-
});
|
|
164
|
-
break;
|
|
165
|
-
case 'configure':
|
|
166
|
-
requests.push({
|
|
167
|
-
protocol: definition.protocol,
|
|
168
|
-
interface: DwnInterfaceName.Protocols,
|
|
169
|
-
method: DwnMethodName.Configure,
|
|
170
|
-
});
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
protocolDefinition: definition,
|
|
176
|
-
permissionScopes: requests,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
export const WalletConnect = { initClient, createPermissionRequestForProtocol };
|
|
180
|
-
//# sourceMappingURL=connect.js.map
|