@enbox/agent 0.1.7 → 0.1.9
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 +11 -11
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/anonymous-dwn-api.js +1 -1
- package/dist/esm/anonymous-dwn-api.js.map +1 -1
- package/dist/esm/connect.js +2 -9
- package/dist/esm/connect.js.map +1 -1
- package/dist/esm/dwn-api.js +144 -195
- package/dist/esm/dwn-api.js.map +1 -1
- package/dist/esm/dwn-protocol-cache.js +149 -0
- package/dist/esm/dwn-protocol-cache.js.map +1 -0
- package/dist/esm/dwn-record-upgrade.js +3 -3
- package/dist/esm/dwn-record-upgrade.js.map +1 -1
- package/dist/esm/hd-identity-vault.js +0 -2
- package/dist/esm/hd-identity-vault.js.map +1 -1
- package/dist/esm/identity-api.js +0 -2
- package/dist/esm/identity-api.js.map +1 -1
- package/dist/esm/permissions-api.js +24 -6
- package/dist/esm/permissions-api.js.map +1 -1
- package/dist/esm/store-data-protocols.js +2 -2
- package/dist/esm/store-data-protocols.js.map +1 -1
- package/dist/esm/test-harness.js +3 -5
- package/dist/esm/test-harness.js.map +1 -1
- package/dist/esm/types/dwn.js.map +1 -1
- package/dist/types/anonymous-dwn-api.d.ts +3 -3
- package/dist/types/anonymous-dwn-api.d.ts.map +1 -1
- package/dist/types/connect.d.ts.map +1 -1
- package/dist/types/dwn-api.d.ts +11 -18
- package/dist/types/dwn-api.d.ts.map +1 -1
- package/dist/types/dwn-protocol-cache.d.ts +76 -0
- package/dist/types/dwn-protocol-cache.d.ts.map +1 -0
- package/dist/types/hd-identity-vault.d.ts.map +1 -1
- package/dist/types/identity-api.d.ts.map +1 -1
- package/dist/types/permissions-api.d.ts.map +1 -1
- package/dist/types/test-harness.d.ts.map +1 -1
- package/dist/types/types/dwn.d.ts +18 -19
- package/dist/types/types/dwn.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/anonymous-dwn-api.ts +4 -4
- package/src/connect.ts +2 -9
- package/src/dwn-api.ts +192 -250
- package/src/dwn-protocol-cache.ts +216 -0
- package/src/dwn-record-upgrade.ts +3 -3
- package/src/hd-identity-vault.ts +0 -2
- package/src/identity-api.ts +0 -2
- package/src/permissions-api.ts +28 -6
- package/src/store-data-protocols.ts +2 -2
- package/src/test-harness.ts +3 -5
- package/src/types/dwn.ts +19 -21
package/src/dwn-api.ts
CHANGED
|
@@ -3,13 +3,13 @@ import type {
|
|
|
3
3
|
DwnConfig,
|
|
4
4
|
EncryptionInput,
|
|
5
5
|
EncryptionKeyDeriver,
|
|
6
|
+
GenericMessage,
|
|
6
7
|
KeyDecrypter,
|
|
7
8
|
ProtocolDefinition,
|
|
8
|
-
ProtocolsQueryReply,
|
|
9
|
-
RecordsQueryReply,
|
|
10
9
|
RecordsWrite,
|
|
11
10
|
RecordsWriteMessage,
|
|
12
11
|
} from '@enbox/dwn-sdk-js';
|
|
12
|
+
import type { DwnSubscriptionHandler, ResubscribeFactory } from '@enbox/dwn-clients';
|
|
13
13
|
import type { KeyIdentifier, PrivateKeyJwk, PublicKeyJwk } from '@enbox/crypto';
|
|
14
14
|
|
|
15
15
|
import { TtlCache } from '@enbox/common';
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
DataStream,
|
|
21
21
|
Dwn,
|
|
22
22
|
DwnMethodName,
|
|
23
|
-
|
|
23
|
+
EventEmitterEventLog,
|
|
24
24
|
Jws,
|
|
25
25
|
KeyDerivationScheme,
|
|
26
26
|
Message,
|
|
@@ -72,7 +72,6 @@ import {
|
|
|
72
72
|
// Import extracted protocol utilities
|
|
73
73
|
import {
|
|
74
74
|
detectNewParticipants as detectNewParticipantsFn,
|
|
75
|
-
hasRelationalReadAccess as hasRelationalReadAccessFn,
|
|
76
75
|
isMultiPartyContext as isMultiPartyContextFn,
|
|
77
76
|
} from './protocol-utils.js';
|
|
78
77
|
|
|
@@ -87,6 +86,13 @@ import {
|
|
|
87
86
|
// Import extracted record upgrade function
|
|
88
87
|
import { upgradeExternalRootRecord as upgradeExternalRootRecordFn } from './dwn-record-upgrade.js';
|
|
89
88
|
|
|
89
|
+
// Import extracted protocol definition fetching functions
|
|
90
|
+
import {
|
|
91
|
+
extractDerivedPublicKey as extractDerivedPublicKeyFn,
|
|
92
|
+
fetchRemoteProtocolDefinition as fetchRemoteProtocolDefinitionFn,
|
|
93
|
+
getProtocolDefinition as getProtocolDefinitionFn,
|
|
94
|
+
} from './dwn-protocol-cache.js';
|
|
95
|
+
|
|
90
96
|
type DwnMessageWithBlob<T extends DwnInterface> = {
|
|
91
97
|
message: DwnMessage[T];
|
|
92
98
|
data?: Blob;
|
|
@@ -184,7 +190,7 @@ export class AgentDwnApi {
|
|
|
184
190
|
}
|
|
185
191
|
|
|
186
192
|
public static async createDwn({
|
|
187
|
-
dataPath, dataStore, didResolver, stateIndex,
|
|
193
|
+
dataPath, dataStore, didResolver, stateIndex, eventLog, messageStore, tenantGate, resumableTaskStore
|
|
188
194
|
}: DwnApiCreateDwnParams): Promise<Dwn> {
|
|
189
195
|
dataStore ??= new DataStoreLevel({ blockstoreLocation: `${dataPath}/DWN_DATASTORE` });
|
|
190
196
|
|
|
@@ -202,9 +208,9 @@ export class AgentDwnApi {
|
|
|
202
208
|
|
|
203
209
|
resumableTaskStore ??= new ResumableTaskStoreLevel({ location: `${dataPath}/DWN_RESUMABLETASKSTORE` });
|
|
204
210
|
|
|
205
|
-
|
|
211
|
+
eventLog ??= new EventEmitterEventLog();
|
|
206
212
|
|
|
207
|
-
return await Dwn.create({ dataStore, didResolver, stateIndex,
|
|
213
|
+
return await Dwn.create({ dataStore, didResolver, stateIndex, eventLog, messageStore, tenantGate, resumableTaskStore });
|
|
208
214
|
}
|
|
209
215
|
|
|
210
216
|
public async processRequest<T extends DwnInterface>(
|
|
@@ -229,122 +235,8 @@ export class AgentDwnApi {
|
|
|
229
235
|
|
|
230
236
|
|
|
231
237
|
// Post-write key delivery: detect new participants and write contextKey records.
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
isDwnRequest(request, DwnInterface.RecordsWrite) &&
|
|
235
|
-
request.encryption &&
|
|
236
|
-
reply.status.code === 202
|
|
237
|
-
) {
|
|
238
|
-
const writeParams = request.messageParams as DwnMessageParams[DwnInterface.RecordsWrite];
|
|
239
|
-
// Skip key-delivery protocol writes to avoid infinite recursion (contextKey records are themselves encrypted)
|
|
240
|
-
if (writeParams.protocol !== KeyDeliveryProtocolDefinition.protocol) {
|
|
241
|
-
try {
|
|
242
|
-
const protocolDefinition = await this.getProtocolDefinition(
|
|
243
|
-
request.target, writeParams.protocol,
|
|
244
|
-
);
|
|
245
|
-
if (protocolDefinition) {
|
|
246
|
-
const recordsWriteMessage = message as unknown as RecordsWriteMessage;
|
|
247
|
-
|
|
248
|
-
// Reactive root-record upgrade (PR E): if this is an externally-authored
|
|
249
|
-
// root record with only ProtocolPath encryption, the owner upgrades it by
|
|
250
|
-
// appending a ProtocolContext recipient entry so that context key
|
|
251
|
-
// holders (including the external author) can also decrypt.
|
|
252
|
-
const authorDid = Jws.getSignerDid(
|
|
253
|
-
recordsWriteMessage.authorization.signature.signatures[0]
|
|
254
|
-
);
|
|
255
|
-
const isExternallyAuthored = authorDid !== request.target;
|
|
256
|
-
const isRootRecord = !writeParams.parentContextId;
|
|
257
|
-
const rootPathSegment = writeParams.protocolPath.split('/')[0];
|
|
258
|
-
const isMultiParty = isMultiPartyContextFn(protocolDefinition, rootPathSegment);
|
|
259
|
-
|
|
260
|
-
if (isExternallyAuthored && isRootRecord && isMultiParty) {
|
|
261
|
-
try {
|
|
262
|
-
await upgradeExternalRootRecordFn(
|
|
263
|
-
this.agent, request.target, recordsWriteMessage,
|
|
264
|
-
this._dwn, this.getSigner.bind(this), this._contextKeyCache,
|
|
265
|
-
);
|
|
266
|
-
} catch (upgradeError: any) {
|
|
267
|
-
console.warn(
|
|
268
|
-
`AgentDwnApi: Reactive root-record upgrade failed for ` +
|
|
269
|
-
`'${recordsWriteMessage.recordId}': ${upgradeError.message}`
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const newParticipants = detectNewParticipantsFn({
|
|
275
|
-
protocolDefinition,
|
|
276
|
-
protocolPath : writeParams.protocolPath,
|
|
277
|
-
recipient : writeParams.recipient,
|
|
278
|
-
tenantDid : request.target,
|
|
279
|
-
authorDid : isExternallyAuthored ? authorDid : undefined,
|
|
280
|
-
});
|
|
238
|
+
await this.postWriteKeyDelivery(request, message, reply);
|
|
281
239
|
|
|
282
|
-
if (newParticipants.size > 0) {
|
|
283
|
-
// Derive the context key to deliver to participants
|
|
284
|
-
const rootContextId = recordsWriteMessage.contextId?.split('/')[0]
|
|
285
|
-
|| recordsWriteMessage.contextId
|
|
286
|
-
|| recordsWriteMessage.recordId;
|
|
287
|
-
|
|
288
|
-
const { keyId, keyUri } = await getEncryptionKeyInfoFn(this.agent, request.target);
|
|
289
|
-
const contextDerivationPath = [
|
|
290
|
-
KeyDerivationScheme.ProtocolContext,
|
|
291
|
-
rootContextId,
|
|
292
|
-
];
|
|
293
|
-
const contextDerivedPrivateKeyBytes =
|
|
294
|
-
await this.agent.keyManager.derivePrivateKeyBytes({
|
|
295
|
-
keyUri,
|
|
296
|
-
derivationPath: contextDerivationPath,
|
|
297
|
-
});
|
|
298
|
-
const contextDerivedPrivateJwk =
|
|
299
|
-
await X25519.bytesToPrivateKey({ privateKeyBytes: contextDerivedPrivateKeyBytes });
|
|
300
|
-
const contextKeyPayload: DerivedPrivateJwk = {
|
|
301
|
-
rootKeyId : keyId,
|
|
302
|
-
derivationScheme : KeyDerivationScheme.ProtocolContext,
|
|
303
|
-
derivationPath : contextDerivationPath,
|
|
304
|
-
derivedPrivateKey : contextDerivedPrivateJwk as PrivateKeyJwk,
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
// Extract the author's key delivery public key from the record
|
|
308
|
-
// so we can encrypt the contextKey directly to the external author.
|
|
309
|
-
const authorKeyDeliveryPubKey =
|
|
310
|
-
recordsWriteMessage.authorization?.authorKeyDeliveryPublicKey;
|
|
311
|
-
|
|
312
|
-
for (const participantDid of newParticipants) {
|
|
313
|
-
try {
|
|
314
|
-
// Use the author's key delivery public key when delivering
|
|
315
|
-
// to the external author; for other participants (e.g.
|
|
316
|
-
// recipient, role holders) fall back to owner-key encryption.
|
|
317
|
-
const recipientKey = (participantDid === authorDid && authorKeyDeliveryPubKey)
|
|
318
|
-
? authorKeyDeliveryPubKey
|
|
319
|
-
: undefined;
|
|
320
|
-
|
|
321
|
-
await this.writeContextKeyRecord({
|
|
322
|
-
tenantDid : request.target,
|
|
323
|
-
recipientDid : participantDid,
|
|
324
|
-
contextKeyData : contextKeyPayload,
|
|
325
|
-
sourceProtocol : writeParams.protocol,
|
|
326
|
-
sourceContextId : rootContextId,
|
|
327
|
-
recipientKeyDeliveryPublicKey : recipientKey,
|
|
328
|
-
});
|
|
329
|
-
} catch (keyDeliveryError: any) {
|
|
330
|
-
console.warn(
|
|
331
|
-
`AgentDwnApi: Key delivery to '${participantDid}' for context ` +
|
|
332
|
-
`'${rootContextId}' failed: ${keyDeliveryError.message}. ` +
|
|
333
|
-
`The participant may not be able to decrypt records in this context.`
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
} catch (detectionError: any) {
|
|
340
|
-
// Participant detection failure is non-fatal — the record is still stored.
|
|
341
|
-
console.warn(
|
|
342
|
-
`AgentDwnApi: Post-write participant detection failed: ` +
|
|
343
|
-
`${detectionError.message}`
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
240
|
// Auto-decrypt reply data if encryption is enabled (Component 7)
|
|
349
241
|
await this.maybeDecryptReply(request, reply);
|
|
350
242
|
|
|
@@ -390,13 +282,30 @@ export class AgentDwnApi {
|
|
|
390
282
|
subscriptionHandler = request.subscriptionHandler;
|
|
391
283
|
}
|
|
392
284
|
|
|
285
|
+
// Build a resubscribe factory for subscribe requests. This closure
|
|
286
|
+
// captures the original request so it can reconstruct and re-sign a new
|
|
287
|
+
// subscribe message with a cursor on reconnection.
|
|
288
|
+
let resubscribeFactory: ResubscribeFactory | undefined;
|
|
289
|
+
if (subscriptionHandler !== undefined && !('messageCid' in request)) {
|
|
290
|
+
resubscribeFactory = async (cursor?: string): Promise<GenericMessage> => {
|
|
291
|
+
const resumeParams = cursor !== undefined
|
|
292
|
+
? { ...request.messageParams, cursor } as DwnMessageParams[T]
|
|
293
|
+
: request.messageParams;
|
|
294
|
+
|
|
295
|
+
const resumeRequest: ProcessDwnRequest<T> = { ...request, messageParams: resumeParams };
|
|
296
|
+
const { message: resumeMessage } = await this.constructDwnMessage({ request: resumeRequest });
|
|
297
|
+
return resumeMessage;
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
393
301
|
// Send the RPC request to the target DID's DWN service endpoint using the Agent's RPC client.
|
|
394
302
|
const reply = await this.sendDwnRpcRequest({
|
|
395
303
|
targetDid: request.target,
|
|
396
304
|
dwnEndpointUrls,
|
|
397
305
|
message,
|
|
398
306
|
data,
|
|
399
|
-
subscriptionHandler
|
|
307
|
+
subscriptionHandler,
|
|
308
|
+
resubscribeFactory,
|
|
400
309
|
});
|
|
401
310
|
|
|
402
311
|
// Auto-decrypt reply data if encryption is enabled (Component 7)
|
|
@@ -410,14 +319,151 @@ export class AgentDwnApi {
|
|
|
410
319
|
return { reply, message, messageCid };
|
|
411
320
|
}
|
|
412
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Post-write key delivery: after a successful encrypted `RecordsWrite`,
|
|
324
|
+
* detect new participants and write `contextKey` records so they can
|
|
325
|
+
* decrypt records in the context.
|
|
326
|
+
*
|
|
327
|
+
* This is a non-fatal operation — if participant detection or key delivery
|
|
328
|
+
* fails, the record is still stored and a warning is logged.
|
|
329
|
+
*/
|
|
330
|
+
private async postWriteKeyDelivery<T extends DwnInterface>(
|
|
331
|
+
request: ProcessDwnRequest<T>,
|
|
332
|
+
message: DwnMessage[T],
|
|
333
|
+
reply: DwnMessageReply[T],
|
|
334
|
+
): Promise<void> {
|
|
335
|
+
if (
|
|
336
|
+
!isDwnRequest(request, DwnInterface.RecordsWrite) ||
|
|
337
|
+
!request.encryption ||
|
|
338
|
+
reply.status.code !== 202
|
|
339
|
+
) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const writeParams = request.messageParams as DwnMessageParams[DwnInterface.RecordsWrite];
|
|
344
|
+
// Skip key-delivery protocol writes to avoid infinite recursion (contextKey records are themselves encrypted)
|
|
345
|
+
if (writeParams.protocol === KeyDeliveryProtocolDefinition.protocol) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const protocolDefinition = await this.getProtocolDefinition(
|
|
351
|
+
request.target, writeParams.protocol,
|
|
352
|
+
);
|
|
353
|
+
if (!protocolDefinition) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const recordsWriteMessage = message as unknown as RecordsWriteMessage;
|
|
358
|
+
|
|
359
|
+
// Reactive root-record upgrade (PR E): if this is an externally-authored
|
|
360
|
+
// root record with only ProtocolPath encryption, the owner upgrades it by
|
|
361
|
+
// appending a ProtocolContext recipient entry so that context key
|
|
362
|
+
// holders (including the external author) can also decrypt.
|
|
363
|
+
const authorDid = Jws.getSignerDid(
|
|
364
|
+
recordsWriteMessage.authorization.signature.signatures[0]
|
|
365
|
+
);
|
|
366
|
+
const isExternallyAuthored = authorDid !== request.target;
|
|
367
|
+
const isRootRecord = !writeParams.parentContextId;
|
|
368
|
+
const rootPathSegment = writeParams.protocolPath.split('/')[0];
|
|
369
|
+
const isMultiParty = isMultiPartyContextFn(protocolDefinition, rootPathSegment);
|
|
370
|
+
|
|
371
|
+
if (isExternallyAuthored && isRootRecord && isMultiParty) {
|
|
372
|
+
try {
|
|
373
|
+
await upgradeExternalRootRecordFn(
|
|
374
|
+
this.agent, request.target, recordsWriteMessage,
|
|
375
|
+
this._dwn, this.getSigner.bind(this), this._contextKeyCache,
|
|
376
|
+
);
|
|
377
|
+
} catch (upgradeError: any) {
|
|
378
|
+
console.warn(
|
|
379
|
+
`AgentDwnApi: Reactive root-record upgrade failed for ` +
|
|
380
|
+
`'${recordsWriteMessage.recordId}': ${upgradeError.message}`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const newParticipants = detectNewParticipantsFn({
|
|
386
|
+
protocolDefinition,
|
|
387
|
+
protocolPath : writeParams.protocolPath,
|
|
388
|
+
recipient : writeParams.recipient,
|
|
389
|
+
tenantDid : request.target,
|
|
390
|
+
authorDid : isExternallyAuthored ? authorDid : undefined,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
if (newParticipants.size > 0) {
|
|
394
|
+
// Derive the context key to deliver to participants
|
|
395
|
+
const rootContextId = recordsWriteMessage.contextId?.split('/')[0]
|
|
396
|
+
|| recordsWriteMessage.contextId
|
|
397
|
+
|| recordsWriteMessage.recordId;
|
|
398
|
+
|
|
399
|
+
const { keyId, keyUri } = await getEncryptionKeyInfoFn(this.agent, request.target);
|
|
400
|
+
const contextDerivationPath = [
|
|
401
|
+
KeyDerivationScheme.ProtocolContext,
|
|
402
|
+
rootContextId,
|
|
403
|
+
];
|
|
404
|
+
const contextDerivedPrivateKeyBytes =
|
|
405
|
+
await this.agent.keyManager.derivePrivateKeyBytes({
|
|
406
|
+
keyUri,
|
|
407
|
+
derivationPath: contextDerivationPath,
|
|
408
|
+
});
|
|
409
|
+
const contextDerivedPrivateJwk =
|
|
410
|
+
await X25519.bytesToPrivateKey({ privateKeyBytes: contextDerivedPrivateKeyBytes });
|
|
411
|
+
const contextKeyPayload: DerivedPrivateJwk = {
|
|
412
|
+
rootKeyId : keyId,
|
|
413
|
+
derivationScheme : KeyDerivationScheme.ProtocolContext,
|
|
414
|
+
derivationPath : contextDerivationPath,
|
|
415
|
+
derivedPrivateKey : contextDerivedPrivateJwk as PrivateKeyJwk,
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// Extract the author's key delivery public key from the record
|
|
419
|
+
// so we can encrypt the contextKey directly to the external author.
|
|
420
|
+
const authorKeyDeliveryPubKey =
|
|
421
|
+
recordsWriteMessage.authorization?.authorKeyDeliveryPublicKey;
|
|
422
|
+
|
|
423
|
+
for (const participantDid of newParticipants) {
|
|
424
|
+
try {
|
|
425
|
+
// Use the author's key delivery public key when delivering
|
|
426
|
+
// to the external author; for other participants (e.g.
|
|
427
|
+
// recipient, role holders) fall back to owner-key encryption.
|
|
428
|
+
const recipientKey = (participantDid === authorDid && authorKeyDeliveryPubKey)
|
|
429
|
+
? authorKeyDeliveryPubKey
|
|
430
|
+
: undefined;
|
|
431
|
+
|
|
432
|
+
await this.writeContextKeyRecord({
|
|
433
|
+
tenantDid : request.target,
|
|
434
|
+
recipientDid : participantDid,
|
|
435
|
+
contextKeyData : contextKeyPayload,
|
|
436
|
+
sourceProtocol : writeParams.protocol,
|
|
437
|
+
sourceContextId : rootContextId,
|
|
438
|
+
recipientKeyDeliveryPublicKey : recipientKey,
|
|
439
|
+
});
|
|
440
|
+
} catch (keyDeliveryError: any) {
|
|
441
|
+
console.warn(
|
|
442
|
+
`AgentDwnApi: Key delivery to '${participantDid}' for context ` +
|
|
443
|
+
`'${rootContextId}' failed: ${keyDeliveryError.message}. ` +
|
|
444
|
+
`The participant may not be able to decrypt records in this context.`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
} catch (detectionError: any) {
|
|
450
|
+
// Participant detection failure is non-fatal — the record is still stored.
|
|
451
|
+
console.warn(
|
|
452
|
+
`AgentDwnApi: Post-write participant detection failed: ` +
|
|
453
|
+
`${detectionError.message}`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
413
458
|
private async sendDwnRpcRequest<T extends DwnInterface>({
|
|
414
|
-
targetDid, dwnEndpointUrls, message, data, subscriptionHandler
|
|
459
|
+
targetDid, dwnEndpointUrls, message, data, subscriptionHandler, resubscribeFactory
|
|
415
460
|
}: {
|
|
416
461
|
targetDid: string;
|
|
417
462
|
dwnEndpointUrls: string[];
|
|
418
463
|
message: DwnMessage[T];
|
|
419
464
|
data?: Blob;
|
|
420
465
|
subscriptionHandler?: MessageHandler[T];
|
|
466
|
+
resubscribeFactory?: ResubscribeFactory;
|
|
421
467
|
}
|
|
422
468
|
): Promise<DwnMessageReply[T]> {
|
|
423
469
|
const errorMessages: { url: string, message: string }[] = [];
|
|
@@ -453,7 +499,10 @@ export class AgentDwnApi {
|
|
|
453
499
|
targetDid,
|
|
454
500
|
message,
|
|
455
501
|
data,
|
|
456
|
-
subscriptionHandler
|
|
502
|
+
subscription: subscriptionHandler ? {
|
|
503
|
+
handler: subscriptionHandler as DwnSubscriptionHandler,
|
|
504
|
+
resubscribeFactory,
|
|
505
|
+
} : undefined,
|
|
457
506
|
});
|
|
458
507
|
|
|
459
508
|
return dwnReply;
|
|
@@ -760,6 +809,9 @@ export class AgentDwnApi {
|
|
|
760
809
|
|
|
761
810
|
// if there is no raw message provided, we need to create the dwn message
|
|
762
811
|
if (!rawMessage) {
|
|
812
|
+
if (request.messageParams === undefined) {
|
|
813
|
+
throw new Error('AgentDwnApi: messageParams must be provided when rawMessage is not given.');
|
|
814
|
+
}
|
|
763
815
|
|
|
764
816
|
// If we need to sign as an author delegate or with permissions we need to get the grantee's signer
|
|
765
817
|
// The messageParams should include either a permissionGrantId, or a delegatedGrant message
|
|
@@ -768,8 +820,7 @@ export class AgentDwnApi {
|
|
|
768
820
|
await this.getSigner(request.author);
|
|
769
821
|
|
|
770
822
|
dwnMessage = await dwnMessageConstructor.create({
|
|
771
|
-
|
|
772
|
-
...request.messageParams!,
|
|
823
|
+
...request.messageParams,
|
|
773
824
|
signer
|
|
774
825
|
});
|
|
775
826
|
|
|
@@ -903,35 +954,6 @@ export class AgentDwnApi {
|
|
|
903
954
|
return getKeyDecrypterFn(this.agent, didUri);
|
|
904
955
|
}
|
|
905
956
|
|
|
906
|
-
/**
|
|
907
|
-
* Checks if a protocol path represents a multi-party context.
|
|
908
|
-
*
|
|
909
|
-
* @param protocolDefinition - The full protocol definition
|
|
910
|
-
* @param rootProtocolPath - The root protocol path to check
|
|
911
|
-
*/
|
|
912
|
-
private isMultiPartyContext(
|
|
913
|
-
protocolDefinition: ProtocolDefinition,
|
|
914
|
-
rootProtocolPath: string,
|
|
915
|
-
): boolean {
|
|
916
|
-
return isMultiPartyContextFn(protocolDefinition, rootProtocolPath);
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
/**
|
|
920
|
-
* Checks if any `$actions` rule in the protocol grants read access
|
|
921
|
-
* via `who: '<actorType>'` and `of: '<path>'`.
|
|
922
|
-
*
|
|
923
|
-
* @param actorType - The actor type to check ('author', 'recipient', or undefined for any)
|
|
924
|
-
* @param ofPath - The protocol path to check
|
|
925
|
-
* @param protocolDefinition - The protocol definition
|
|
926
|
-
*/
|
|
927
|
-
private hasRelationalReadAccess(
|
|
928
|
-
actorType: 'author' | 'recipient' | undefined,
|
|
929
|
-
ofPath: string,
|
|
930
|
-
protocolDefinition: ProtocolDefinition,
|
|
931
|
-
): boolean {
|
|
932
|
-
return hasRelationalReadAccessFn(actorType, ofPath, protocolDefinition);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
957
|
/**
|
|
936
958
|
* Analyses a record write to determine which DIDs need context key delivery.
|
|
937
959
|
*
|
|
@@ -949,7 +971,7 @@ export class AgentDwnApi {
|
|
|
949
971
|
}
|
|
950
972
|
|
|
951
973
|
/**
|
|
952
|
-
|
|
974
|
+
* Fetches a protocol definition from the local DWN, with caching.
|
|
953
975
|
* Returns undefined if the protocol is not installed.
|
|
954
976
|
*
|
|
955
977
|
* @param tenantDid - The tenant DID to query
|
|
@@ -958,33 +980,12 @@ export class AgentDwnApi {
|
|
|
958
980
|
*/
|
|
959
981
|
private async getProtocolDefinition(
|
|
960
982
|
tenantDid: string,
|
|
961
|
-
protocolUri: string
|
|
983
|
+
protocolUri: string,
|
|
962
984
|
): Promise<ProtocolDefinition | undefined> {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (cached) {
|
|
967
|
-
return cached;
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
const signer = await this.getSigner(tenantDid);
|
|
971
|
-
const protocolsQuery = await dwnMessageConstructors[
|
|
972
|
-
DwnInterface.ProtocolsQuery
|
|
973
|
-
].create({
|
|
974
|
-
filter: { protocol: protocolUri },
|
|
975
|
-
signer,
|
|
976
|
-
});
|
|
977
|
-
|
|
978
|
-
const reply = await this._dwn.processMessage(
|
|
979
|
-
tenantDid, protocolsQuery.message,
|
|
985
|
+
return getProtocolDefinitionFn(
|
|
986
|
+
tenantDid, protocolUri, this._dwn,
|
|
987
|
+
this.getSigner.bind(this), this._protocolDefinitionCache,
|
|
980
988
|
);
|
|
981
|
-
if (reply.status.code !== 200 || !reply.entries?.length) {
|
|
982
|
-
return undefined;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
const definition = reply.entries[0].descriptor.definition;
|
|
986
|
-
this._protocolDefinitionCache.set(cacheKey, definition);
|
|
987
|
-
return definition;
|
|
988
989
|
}
|
|
989
990
|
|
|
990
991
|
/**
|
|
@@ -995,34 +996,10 @@ export class AgentDwnApi {
|
|
|
995
996
|
targetDid: string,
|
|
996
997
|
protocolUri: string,
|
|
997
998
|
): Promise<ProtocolDefinition> {
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
const protocolsQuery = await dwnMessageConstructors[
|
|
1003
|
-
DwnInterface.ProtocolsQuery
|
|
1004
|
-
].create({
|
|
1005
|
-
filter: { protocol: protocolUri },
|
|
1006
|
-
});
|
|
1007
|
-
|
|
1008
|
-
const reply = await this.sendDwnRpcRequest({
|
|
1009
|
-
targetDid,
|
|
1010
|
-
dwnEndpointUrls: await getDwnServiceEndpointUrls(
|
|
1011
|
-
targetDid, this.agent.did,
|
|
1012
|
-
),
|
|
1013
|
-
message: protocolsQuery.message,
|
|
1014
|
-
}) as ProtocolsQueryReply;
|
|
1015
|
-
|
|
1016
|
-
if (reply.status.code !== 200 || !reply.entries?.length) {
|
|
1017
|
-
throw new Error(
|
|
1018
|
-
`AgentDwnApi: Failed to fetch protocol '${protocolUri}' from ` +
|
|
1019
|
-
`'${targetDid}'. The recipient may not have the protocol installed.`
|
|
1020
|
-
);
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
const definition = reply.entries[0].descriptor.definition;
|
|
1024
|
-
this._protocolDefinitionCache.set(cacheKey, definition);
|
|
1025
|
-
return definition;
|
|
999
|
+
return fetchRemoteProtocolDefinitionFn(
|
|
1000
|
+
targetDid, protocolUri, this.agent.did,
|
|
1001
|
+
this.sendDwnRpcRequest.bind(this), this._protocolDefinitionCache,
|
|
1002
|
+
);
|
|
1026
1003
|
}
|
|
1027
1004
|
|
|
1028
1005
|
/**
|
|
@@ -1043,46 +1020,11 @@ export class AgentDwnApi {
|
|
|
1043
1020
|
rootContextId: string,
|
|
1044
1021
|
requesterDid: string,
|
|
1045
1022
|
): Promise<{ rootKeyId: string; derivedPublicKey: PublicKeyJwk } | undefined> {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
filter: {
|
|
1052
|
-
protocol : protocolUri,
|
|
1053
|
-
contextId : rootContextId,
|
|
1054
|
-
},
|
|
1055
|
-
});
|
|
1056
|
-
|
|
1057
|
-
const dwnEndpointUrls = await getDwnServiceEndpointUrls(targetDid, this.agent.did);
|
|
1058
|
-
const queryReply = await this.sendDwnRpcRequest<DwnInterface.RecordsQuery>({
|
|
1059
|
-
targetDid,
|
|
1060
|
-
dwnEndpointUrls,
|
|
1061
|
-
message: recordsQuery.message,
|
|
1062
|
-
}) as RecordsQueryReply;
|
|
1063
|
-
|
|
1064
|
-
if (queryReply.status.code !== 200 || !queryReply.entries?.length) {
|
|
1065
|
-
return undefined;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
// Search entries for one with a ProtocolContext recipient entry
|
|
1069
|
-
// that includes derivedPublicKey
|
|
1070
|
-
for (const entry of queryReply.entries) {
|
|
1071
|
-
if (entry.encryption?.recipients) {
|
|
1072
|
-
const contextEntry = entry.encryption.recipients.find(
|
|
1073
|
-
(r: { header: { derivationScheme: string; derivedPublicKey?: PublicKeyJwk } }) =>
|
|
1074
|
-
r.header.derivationScheme === KeyDerivationScheme.ProtocolContext && r.header.derivedPublicKey
|
|
1075
|
-
);
|
|
1076
|
-
if (contextEntry?.header.derivedPublicKey) {
|
|
1077
|
-
return {
|
|
1078
|
-
rootKeyId : contextEntry.header.kid,
|
|
1079
|
-
derivedPublicKey : contextEntry.header.derivedPublicKey,
|
|
1080
|
-
};
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
return undefined;
|
|
1023
|
+
return extractDerivedPublicKeyFn(
|
|
1024
|
+
targetDid, protocolUri, rootContextId, requesterDid,
|
|
1025
|
+
this.agent.did, this.getSigner.bind(this),
|
|
1026
|
+
this.sendDwnRpcRequest.bind(this),
|
|
1027
|
+
);
|
|
1086
1028
|
}
|
|
1087
1029
|
|
|
1088
1030
|
/**
|