@keyneom/sync-kit 0.2.0-rc.1 → 0.2.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +8 -3
  2. package/dist/sharing/appdata-identity-store.d.ts +36 -0
  3. package/dist/sharing/appdata-identity-store.d.ts.map +1 -0
  4. package/dist/sharing/appdata-identity-store.js +49 -0
  5. package/dist/sharing/appdata-identity-store.js.map +1 -0
  6. package/dist/sharing/control.d.ts +136 -0
  7. package/dist/sharing/control.d.ts.map +1 -0
  8. package/dist/sharing/control.js +486 -0
  9. package/dist/sharing/control.js.map +1 -0
  10. package/dist/sharing/controller.d.ts +79 -3
  11. package/dist/sharing/controller.d.ts.map +1 -1
  12. package/dist/sharing/controller.js +340 -117
  13. package/dist/sharing/controller.js.map +1 -1
  14. package/dist/sharing/index.d.ts +2 -0
  15. package/dist/sharing/index.d.ts.map +1 -1
  16. package/dist/sharing/index.js +2 -0
  17. package/dist/sharing/index.js.map +1 -1
  18. package/dist/sharing/link-exchange.d.ts +51 -0
  19. package/dist/sharing/link-exchange.d.ts.map +1 -0
  20. package/dist/sharing/link-exchange.js +131 -0
  21. package/dist/sharing/link-exchange.js.map +1 -0
  22. package/dist/sharing/migrating-identity-store.d.ts +29 -0
  23. package/dist/sharing/migrating-identity-store.d.ts.map +1 -0
  24. package/dist/sharing/migrating-identity-store.js +37 -0
  25. package/dist/sharing/migrating-identity-store.js.map +1 -0
  26. package/dist/sharing/transport.d.ts +2 -0
  27. package/dist/sharing/transport.d.ts.map +1 -1
  28. package/dist/stores/google-drive/index.d.ts +20 -0
  29. package/dist/stores/google-drive/index.d.ts.map +1 -1
  30. package/dist/stores/google-drive/index.js +64 -0
  31. package/dist/stores/google-drive/index.js.map +1 -1
  32. package/dist/stores/google-drive/picker.d.ts +20 -0
  33. package/dist/stores/google-drive/picker.d.ts.map +1 -1
  34. package/dist/stores/google-drive/picker.js +64 -0
  35. package/dist/stores/google-drive/picker.js.map +1 -1
  36. package/dist/stores/google-drive/sharing.d.ts +2 -0
  37. package/dist/stores/google-drive/sharing.d.ts.map +1 -1
  38. package/dist/stores/google-drive/sharing.js +83 -6
  39. package/dist/stores/google-drive/sharing.js.map +1 -1
  40. package/package.json +20 -3
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/sharing/controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/sharing/controller.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,13 @@ import { canAdministerSharedBackup, parseSharedBackupEnvelopeV1, sharedBackupPar
3
3
  import { acceptSharingPublicKeyResponseV1, createSharedBackupEnvelopeV1, createSharingInvitationV1, createSharingPublicKeyResponseV1, decryptSharedBackupEnvelopeV1, verifySharedBackupEnvelopeV1, verifySharingInvitationV1, } from "./web-crypto.js";
4
4
  import { formatSharingInviteEmailMessage, appendSharingJoinParams } from "./join.js";
5
5
  export { IndexedDbSharedBackupRegistry } from "./registry-indexeddb.js";
6
+ /**
7
+ * Placeholder recipient permission id for the link-carried exchange, which has
8
+ * no Drive exchange grant. Set identically on the invitation and passed to
9
+ * accept, so the anti-substitution equality check passes; account provenance in
10
+ * this flow comes from the response signature, not a Drive permission.
11
+ */
12
+ const SHARING_LINK_PERMISSION_ID = "link";
6
13
  export class MemorySharedBackupRegistry {
7
14
  records = new Map();
8
15
  get(datasetId) {
@@ -36,6 +43,25 @@ export class SharedBackupController {
36
43
  listDatasets() {
37
44
  return this.options.transport.listDatasets();
38
45
  }
46
+ /** Returns the locally pinned genesis owner for a previously trusted dataset. */
47
+ async getDatasetTrust(datasetId) {
48
+ const record = await this.requiredRegistry(datasetId);
49
+ return { trustedOwnerKeyId: record.trustedOwnerKeyId };
50
+ }
51
+ /**
52
+ * Reads the verified cryptographic membership for a dataset. This is useful
53
+ * for a companion control ledger that mirrors key provenance and user-facing
54
+ * contact metadata without making application payloads authoritative.
55
+ */
56
+ async getDatasetParticipants(datasetId) {
57
+ const stored = await this.readDatasetById(datasetId);
58
+ const record = await this.requiredRegistry(datasetId);
59
+ await this.verifyHead(stored, record);
60
+ return {
61
+ trustedOwnerKeyId: record.trustedOwnerKeyId,
62
+ participants: sharedBackupParticipants(stored.envelope),
63
+ };
64
+ }
39
65
  createDataset(datasetId, value) {
40
66
  return this.serialized(async () => {
41
67
  requireNonEmpty(datasetId, "datasetId");
@@ -56,6 +82,49 @@ export class SharedBackupController {
56
82
  return result(stored, value, "created");
57
83
  });
58
84
  }
85
+ /**
86
+ * Reconnect to an existing dataset this identity can already decrypt —
87
+ * e.g. after a reinstall, or when an interrupted setup left the remote
88
+ * file without a local registry record. The owner key is pinned from the
89
+ * envelope itself (trust-on-first-use), so callers recovering their own
90
+ * dataset should pass `requireOwned` to insist this identity is the
91
+ * dataset's owner.
92
+ */
93
+ adoptDataset(datasetId, options) {
94
+ return this.serialized(async () => {
95
+ const stored = await this.readDatasetById(datasetId);
96
+ const previous = await this.options.registry.get(datasetId);
97
+ const record = previous ?? this.initialOwnerRecord(stored);
98
+ await verifySharedBackupEnvelopeV1(stored.envelope, this.crypto(), {
99
+ trustedOwnerKeyId: record.trustedOwnerKeyId,
100
+ });
101
+ const identity = await this.options.identity();
102
+ if (options?.requireOwned) {
103
+ const self = sharedBackupParticipant(stored.envelope, identity.publicKey.keyId);
104
+ if (self?.role !== "owner") {
105
+ throw new SyncKitError("authorization", `This identity does not own dataset ${datasetId}.`);
106
+ }
107
+ }
108
+ const value = await decryptSharedBackupEnvelopeV1(stored.envelope, this.options.codec, identity, this.crypto(), { trustedOwnerKeyId: record.trustedOwnerKeyId });
109
+ await this.persistHead(stored, record.trustedOwnerKeyId, previous ?? undefined);
110
+ return result(stored, value, "adopted");
111
+ });
112
+ }
113
+ /** Delete a dataset file from the transport and forget its local record. */
114
+ deleteDataset(datasetId) {
115
+ return this.serialized(async () => {
116
+ requireNonEmpty(datasetId, "datasetId");
117
+ const fileId = (await this.options.registry.get(datasetId))?.fileId ??
118
+ (await this.options.transport.listDatasets()).find((candidate) => candidate.datasetId === datasetId)?.fileId;
119
+ if (fileId) {
120
+ if (!this.options.transport.deleteDataset) {
121
+ throw new SyncKitError("state", "This transport does not support deleting datasets.");
122
+ }
123
+ await this.options.transport.deleteDataset(fileId);
124
+ }
125
+ await this.options.registry.delete(datasetId);
126
+ });
127
+ }
59
128
  loadDataset(datasetId) {
60
129
  return this.serialized(async () => {
61
130
  const stored = await this.readDatasetById(datasetId);
@@ -246,128 +315,302 @@ export class SharedBackupController {
246
315
  if (verifiedAccount) {
247
316
  await this.options.transport.deleteExchange(input.responseFileId);
248
317
  }
249
- const results = [];
250
- for (const grant of accepted) {
251
- try {
252
- const stored = await this.readDatasetById(grant.datasetId);
253
- const record = (await this.options.registry.get(grant.datasetId)) ??
254
- this.initialOwnerRecord(stored);
255
- await this.verifyHead(stored, record);
256
- const value = await decryptSharedBackupEnvelopeV1(stored.envelope, this.options.codec, identity, this.crypto(), { trustedOwnerKeyId: record.trustedOwnerKeyId });
257
- const participants = participantInputs(stored.envelope).filter((participant) => participant.publicKey.keyId !==
258
- grant.participant.publicKey.keyId);
259
- participants.push(grant.participant);
260
- const next = await createSharedBackupEnvelopeV1(value, this.options.codec, identity, {
261
- appId: this.options.appId,
262
- backupId: grant.datasetId,
263
- participants,
264
- previous: stored.envelope,
265
- }, this.cryptoOptions());
266
- const updated = await this.options.transport.writeDataset(stored, next);
267
- const permission = await this.options.transport.setDatasetPermission(stored.fileId, input.recipientEmailAddress, grant.participant.role === "owner"
268
- ? "admin"
269
- : grant.participant.role, {
270
- hasInheritedReadAccess: true,
271
- });
272
- const participantPermissionIds = {
273
- ...record.participantPermissionIds,
274
- ...(permission.permissionId
275
- ? {
276
- [grant.participant.publicKey.keyId]: permission.permissionId,
277
- }
278
- : {}),
279
- };
280
- const nextRecord = {
281
- ...record,
282
- fileId: updated.fileId,
283
- lastRevisionId: updated.envelope.revisionId,
284
- participantPermissionIds,
285
- };
286
- await this.options.registry.set(nextRecord);
287
- results.push({
288
- datasetId: grant.datasetId,
289
- fileId: updated.fileId,
290
- revisionId: updated.envelope.revisionId,
291
- ...(permission.permissionId
292
- ? { permissionId: permission.permissionId }
293
- : {}),
294
- status: "accepted",
295
- });
318
+ return this.applyAcceptedGrants(accepted, identity, input.recipientEmailAddress);
319
+ });
320
+ }
321
+ /**
322
+ * Applies verified acceptance grants to each dataset: add the participant,
323
+ * re-encrypt, write, and per-email-share the dataset file. Shared by the
324
+ * Drive-file accept path and the link-payload accept path.
325
+ */
326
+ async applyAcceptedGrants(accepted, identity, recipientEmailAddress) {
327
+ const results = [];
328
+ for (const grant of accepted) {
329
+ try {
330
+ const stored = await this.readDatasetById(grant.datasetId);
331
+ const codec = this.codecForDataset(grant.datasetId);
332
+ const record = (await this.options.registry.get(grant.datasetId)) ??
333
+ this.initialOwnerRecord(stored);
334
+ await this.verifyHead(stored, record);
335
+ const value = await decryptSharedBackupEnvelopeV1(stored.envelope, codec, identity, this.crypto(), { trustedOwnerKeyId: record.trustedOwnerKeyId });
336
+ const participants = participantInputs(stored.envelope).filter((participant) => participant.publicKey.keyId !==
337
+ grant.participant.publicKey.keyId);
338
+ participants.push(grant.participant);
339
+ const next = await createSharedBackupEnvelopeV1(value, codec, identity, {
340
+ appId: this.options.appId,
341
+ backupId: grant.datasetId,
342
+ participants,
343
+ previous: stored.envelope,
344
+ }, this.cryptoOptions());
345
+ const updated = await this.options.transport.writeDataset(stored, next);
346
+ const permission = await this.options.transport.setDatasetPermission(stored.fileId, recipientEmailAddress, grant.participant.role === "owner"
347
+ ? "admin"
348
+ : grant.participant.role, {
349
+ hasInheritedReadAccess: true,
350
+ });
351
+ const participantPermissionIds = {
352
+ ...record.participantPermissionIds,
353
+ ...(permission.permissionId
354
+ ? {
355
+ [grant.participant.publicKey.keyId]: permission.permissionId,
356
+ }
357
+ : {}),
358
+ };
359
+ const nextRecord = {
360
+ ...record,
361
+ fileId: updated.fileId,
362
+ lastRevisionId: updated.envelope.revisionId,
363
+ participantPermissionIds,
364
+ };
365
+ await this.options.registry.set(nextRecord);
366
+ results.push({
367
+ datasetId: grant.datasetId,
368
+ fileId: updated.fileId,
369
+ revisionId: updated.envelope.revisionId,
370
+ ...(permission.permissionId
371
+ ? { permissionId: permission.permissionId }
372
+ : {}),
373
+ status: "accepted",
374
+ });
375
+ }
376
+ catch (error) {
377
+ results.push({
378
+ datasetId: grant.datasetId,
379
+ status: "failed",
380
+ error,
381
+ });
382
+ }
383
+ }
384
+ return results;
385
+ }
386
+ /**
387
+ * Owner side of the link-carried exchange. Shares each granted dataset file
388
+ * with the recipient's email (so it lands in their Picker) and returns the
389
+ * signed invitation plus the file list to embed in the join link. Unlike
390
+ * {@link inviteParticipant} it writes no Drive `exchanges/` invitation file.
391
+ */
392
+ inviteParticipantForLink(input) {
393
+ return this.serialized(async () => {
394
+ const identity = await this.options.identity();
395
+ const trustedOwnerKeyIds = new Set();
396
+ const files = [];
397
+ for (const grant of input.requestedGrants) {
398
+ const stored = await this.readDatasetById(grant.datasetId);
399
+ const record = await this.requiredRegistry(grant.datasetId);
400
+ const verified = await verifySharedBackupEnvelopeV1(stored.envelope, this.crypto(), { trustedOwnerKeyId: record.trustedOwnerKeyId });
401
+ const actor = sharedBackupParticipant(verified, identity.publicKey.keyId);
402
+ if (!actor || !canAdministerSharedBackup(actor.role)) {
403
+ throw new SyncKitError("authorization", `This identity cannot invite participants to ${grant.datasetId}.`);
296
404
  }
297
- catch (error) {
298
- results.push({
405
+ trustedOwnerKeyIds.add(record.trustedOwnerKeyId);
406
+ await this.options.transport.setDatasetPermission(stored.fileId, input.emailAddress, grant.role, { hasInheritedReadAccess: false });
407
+ files.push({
408
+ datasetId: grant.datasetId,
409
+ fileId: stored.fileId,
410
+ role: grant.role,
411
+ });
412
+ }
413
+ if (trustedOwnerKeyIds.size !== 1) {
414
+ throw new SyncKitError("configuration", "One invitation cannot combine datasets with different owner trust roots.");
415
+ }
416
+ const trustedOwnerKeyId = [...trustedOwnerKeyIds][0];
417
+ if (!trustedOwnerKeyId) {
418
+ throw new SyncKitError("state", "The invitation has no owner trust root.");
419
+ }
420
+ const storage = await this.options.transport.ensureStorage();
421
+ const invitation = await createSharingInvitationV1(identity, {
422
+ appId: this.options.appId,
423
+ appFolderId: storage.appFolderId,
424
+ recipientDrivePermissionId: SHARING_LINK_PERMISSION_ID,
425
+ requestedGrants: input.requestedGrants,
426
+ trustedOwnerKeyId,
427
+ ...(input.expiresAt ? { expiresAt: input.expiresAt } : {}),
428
+ }, this.cryptoOptions());
429
+ return { invitation, files };
430
+ });
431
+ }
432
+ /**
433
+ * Recipient side of the link-carried exchange. Verifies the invitation carried
434
+ * in the join link, registers the granted datasets with their file ids (so
435
+ * later reads/writes resolve without listing the owner's folder), and returns
436
+ * a signed key response to send back to the owner. Reads/writes no Drive
437
+ * `exchanges/` file.
438
+ */
439
+ submitKeyResponseFromInvitation(invitation, datasetFiles) {
440
+ return this.serialized(async () => {
441
+ const verified = await verifySharingInvitationV1(invitation, {
442
+ crypto: this.crypto(),
443
+ ...(this.options.now ? { now: this.options.now } : {}),
444
+ });
445
+ if (verified.appId !== this.options.appId) {
446
+ throw new SyncKitError("compatibility", "The invitation belongs to another app.");
447
+ }
448
+ const identity = await this.options.identity();
449
+ const accountBinding = await this.options.createAccountBinding?.({
450
+ appId: this.options.appId,
451
+ exchangeId: verified.exchangeId,
452
+ sharingKeyId: identity.publicKey.keyId,
453
+ });
454
+ const response = await createSharingPublicKeyResponseV1(identity, {
455
+ appId: this.options.appId,
456
+ exchangeId: verified.exchangeId,
457
+ ...(accountBinding ? { accountBinding } : {}),
458
+ }, this.cryptoOptions());
459
+ const fileById = new Map(datasetFiles.map((file) => [file.datasetId, file.fileId]));
460
+ for (const grant of verified.requestedGrants) {
461
+ const existing = await this.options.registry.get(grant.datasetId);
462
+ if (existing &&
463
+ existing.trustedOwnerKeyId !== verified.trustedOwnerKeyId) {
464
+ throw new SyncKitError("conflict", `Dataset ${grant.datasetId} is already pinned to another owner key.`);
465
+ }
466
+ const fileId = fileById.get(grant.datasetId);
467
+ await this.options.registry.set(existing
468
+ ? { ...existing, ...(fileId ? { fileId } : {}) }
469
+ : {
299
470
  datasetId: grant.datasetId,
300
- status: "failed",
301
- error,
471
+ ...(fileId ? { fileId } : {}),
472
+ trustedOwnerKeyId: verified.trustedOwnerKeyId,
302
473
  });
303
- }
304
474
  }
305
- return results;
475
+ return response;
476
+ });
477
+ }
478
+ /**
479
+ * Owner side. Accepts a key response carried in a response link (no Drive
480
+ * `exchanges/` read), adds the recipient to each granted dataset, and
481
+ * per-email shares the dataset files (re-affirming the invite-time share).
482
+ * The invitation must be supplied by the caller (persisted at invite time,
483
+ * keyed by exchange id).
484
+ */
485
+ acceptKeyResponseFromPayload(input) {
486
+ return this.serialized(async () => {
487
+ const identity = await this.options.identity();
488
+ const binding = input.response.accountBinding;
489
+ if (this.options.requireAccountBinding && !binding) {
490
+ throw new SyncKitError("authorization", "The key response has no required account binding.");
491
+ }
492
+ const verifiedAccount = binding && this.options.verifyAccountBinding
493
+ ? await this.options.verifyAccountBinding(binding, {
494
+ appId: this.options.appId,
495
+ exchangeId: input.invitation.exchangeId,
496
+ sharingKeyId: input.response.keyId,
497
+ credentialId: binding.passkey.credentialId,
498
+ })
499
+ : undefined;
500
+ const accepted = await acceptSharingPublicKeyResponseV1(input.invitation, input.response, {
501
+ acceptedByKeyId: identity.publicKey.keyId,
502
+ drivePermissionId: input.invitation.recipientDrivePermissionId,
503
+ ...(verifiedAccount ? { googleSubject: verifiedAccount.subject } : {}),
504
+ }, this.cryptoOptions());
505
+ return this.applyAcceptedGrants(accepted, identity, input.recipientEmailAddress);
306
506
  });
307
507
  }
308
508
  setDatasetRole(input) {
309
- return this.changeParticipants(input.datasetId, (stored, value) => {
509
+ return this.serialized(async () => {
510
+ requireNonEmpty(input.datasetId, "datasetId");
511
+ requireNonEmpty(input.keyId, "keyId");
512
+ requireNonEmpty(input.emailAddress, "emailAddress");
513
+ const stored = await this.readDatasetById(input.datasetId);
514
+ const record = await this.requiredRegistry(input.datasetId);
515
+ await this.verifyHead(stored, record);
516
+ const identity = await this.options.identity();
517
+ const actor = sharedBackupParticipant(stored.envelope, identity.publicKey.keyId);
518
+ if (!actor || !canAdministerSharedBackup(actor.role)) {
519
+ throw new SyncKitError("authorization", "Only a current owner or admin can change dataset access.");
520
+ }
521
+ const value = await decryptSharedBackupEnvelopeV1(stored.envelope, this.options.codec, identity, this.crypto(), { trustedOwnerKeyId: record.trustedOwnerKeyId });
310
522
  const participants = participantInputs(stored.envelope);
311
523
  const participant = participants.find((candidate) => candidate.publicKey.keyId === input.keyId);
312
524
  if (!participant) {
313
525
  throw new SyncKitError("not-found", `Participant ${input.keyId} is not in this dataset.`);
314
526
  }
315
527
  participant.role = input.role;
316
- return Promise.resolve({
528
+ const existingPermission = await this.findDirectDatasetPermission(stored.fileId, record.participantPermissionIds?.[input.keyId], input.emailAddress);
529
+ const permission = await this.options.transport.setDatasetPermission(stored.fileId, input.emailAddress, input.role, {
530
+ ...(existingPermission
531
+ ? { existingDirectPermissionId: existingPermission.permissionId }
532
+ : {}),
533
+ ...(input.role === "viewer"
534
+ ? { hasInheritedReadAccess: true }
535
+ : {}),
536
+ });
537
+ const participantPermissionIds = permission.permissionId
538
+ ? {
539
+ ...record.participantPermissionIds,
540
+ [input.keyId]: permission.permissionId,
541
+ }
542
+ : Object.fromEntries(Object.entries(record.participantPermissionIds ?? {}).filter(([keyId]) => keyId !== input.keyId));
543
+ const next = await createSharedBackupEnvelopeV1(value, this.options.codec, identity, {
544
+ appId: this.options.appId,
545
+ backupId: input.datasetId,
317
546
  participants,
318
- value,
319
- afterWrite: async (updated, record) => {
320
- const permission = await this.options.transport.setDatasetPermission(updated.fileId, input.emailAddress, input.role, {
321
- ...(record.participantPermissionIds?.[input.keyId]
322
- ? {
323
- existingDirectPermissionId: record.participantPermissionIds[input.keyId],
324
- }
325
- : {}),
326
- ...(input.role === "viewer"
327
- ? { hasInheritedReadAccess: true }
328
- : {}),
329
- });
330
- const participantPermissionIds = {
331
- ...record.participantPermissionIds,
332
- ...(permission.permissionId
333
- ? { [input.keyId]: permission.permissionId }
334
- : {}),
335
- };
336
- await this.options.registry.set({
337
- ...record,
338
- participantPermissionIds,
339
- });
340
- },
547
+ previous: stored.envelope,
548
+ }, this.cryptoOptions());
549
+ const updated = await this.options.transport.writeDataset(stored, next);
550
+ await this.persistHead(updated, record.trustedOwnerKeyId, {
551
+ ...record,
552
+ participantPermissionIds,
341
553
  });
554
+ return result(updated, value, "updated");
342
555
  });
343
556
  }
344
557
  revokeDatasetKey(input) {
345
- return this.changeParticipants(input.datasetId, (stored, value) => {
558
+ return this.serialized(async () => {
559
+ const stored = await this.readDatasetById(input.datasetId);
560
+ const record = await this.requiredRegistry(input.datasetId);
561
+ await this.verifyHead(stored, record);
562
+ const identity = await this.options.identity();
563
+ const actor = sharedBackupParticipant(stored.envelope, identity.publicKey.keyId);
564
+ if (!actor || !canAdministerSharedBackup(actor.role)) {
565
+ throw new SyncKitError("authorization", "Only a current owner or admin can change dataset access.");
566
+ }
567
+ const value = await decryptSharedBackupEnvelopeV1(stored.envelope, this.options.codec, identity, this.crypto(), { trustedOwnerKeyId: record.trustedOwnerKeyId });
346
568
  const participant = sharedBackupParticipant(stored.envelope, input.keyId);
347
569
  if (!participant) {
348
- throw new SyncKitError("not-found", `Participant ${input.keyId} is not in this dataset.`);
570
+ const permission = await this.findDirectDatasetPermission(stored.fileId, record.participantPermissionIds?.[input.keyId], input.emailAddress);
571
+ if (permission) {
572
+ await this.options.transport.removeDatasetPermission(stored.fileId, permission.permissionId);
573
+ }
574
+ await this.options.registry.set({
575
+ ...record,
576
+ participantPermissionIds: Object.fromEntries(Object.entries(record.participantPermissionIds ?? {}).filter(([keyId]) => keyId !== input.keyId)),
577
+ });
578
+ return result(stored, value, "unchanged");
349
579
  }
350
580
  if (participant.role === "owner") {
351
581
  throw new SyncKitError("authorization", "Owner transfer or removal is not supported by sharing v1.");
352
582
  }
583
+ const permission = await this.findDirectDatasetPermission(stored.fileId, record.participantPermissionIds?.[input.keyId], input.emailAddress);
584
+ if (permission) {
585
+ await this.options.transport.removeDatasetPermission(stored.fileId, permission.permissionId);
586
+ }
353
587
  const participants = participantInputs(stored.envelope).filter((candidate) => candidate.publicKey.keyId !== input.keyId);
354
- return Promise.resolve({
588
+ const next = await createSharedBackupEnvelopeV1(value, this.options.codec, identity, {
589
+ appId: this.options.appId,
590
+ backupId: input.datasetId,
355
591
  participants,
356
- value,
357
- afterWrite: async (_updated, record) => {
358
- const permissionId = record.participantPermissionIds?.[input.keyId];
359
- if (permissionId) {
360
- await this.options.transport.removeDatasetPermission(stored.fileId, permissionId);
361
- }
362
- const participantPermissionIds = Object.fromEntries(Object.entries(record.participantPermissionIds ?? {}).filter(([keyId]) => keyId !== input.keyId));
363
- await this.options.registry.set({
364
- ...record,
365
- participantPermissionIds,
366
- });
367
- },
592
+ previous: stored.envelope,
593
+ }, this.cryptoOptions());
594
+ const updated = await this.options.transport.writeDataset(stored, next);
595
+ await this.persistHead(updated, record.trustedOwnerKeyId, {
596
+ ...record,
597
+ participantPermissionIds: Object.fromEntries(Object.entries(record.participantPermissionIds ?? {}).filter(([keyId]) => keyId !== input.keyId)),
368
598
  });
599
+ return result(updated, value, "updated");
369
600
  });
370
601
  }
602
+ async findDirectDatasetPermission(fileId, permissionId, emailAddress) {
603
+ const directPermissions = (await this.options.transport.listDatasetPermissions(fileId)).filter((permission) => !permission.inherited);
604
+ if (permissionId) {
605
+ const byId = directPermissions.find((permission) => permission.permissionId === permissionId);
606
+ if (byId)
607
+ return byId;
608
+ }
609
+ const normalizedEmail = emailAddress?.trim().toLowerCase();
610
+ if (!normalizedEmail)
611
+ return undefined;
612
+ return directPermissions.find((permission) => permission.emailAddress?.toLowerCase() === normalizedEmail);
613
+ }
371
614
  rotateLocalKey(replacementIdentity, datasetIds) {
372
615
  return this.serialized(async () => {
373
616
  const currentIdentity = await this.options.identity();
@@ -544,30 +787,6 @@ export class SharedBackupController {
544
787
  return { datasetId: input.datasetId, actions };
545
788
  });
546
789
  }
547
- changeParticipants(datasetId, change) {
548
- return this.serialized(async () => {
549
- const stored = await this.readDatasetById(datasetId);
550
- const record = await this.requiredRegistry(datasetId);
551
- await this.verifyHead(stored, record);
552
- const identity = await this.options.identity();
553
- const actor = sharedBackupParticipant(stored.envelope, identity.publicKey.keyId);
554
- if (!actor || !canAdministerSharedBackup(actor.role)) {
555
- throw new SyncKitError("authorization", "Only a current owner or admin can change dataset access.");
556
- }
557
- const value = await decryptSharedBackupEnvelopeV1(stored.envelope, this.options.codec, identity, this.crypto(), { trustedOwnerKeyId: record.trustedOwnerKeyId });
558
- const changed = await change(stored, value);
559
- const next = await createSharedBackupEnvelopeV1(changed.value, this.options.codec, identity, {
560
- appId: this.options.appId,
561
- backupId: datasetId,
562
- participants: changed.participants,
563
- previous: stored.envelope,
564
- }, this.cryptoOptions());
565
- const updated = await this.options.transport.writeDataset(stored, next);
566
- const nextRecord = await this.persistHead(updated, record.trustedOwnerKeyId, record);
567
- await changed.afterWrite?.(updated, nextRecord);
568
- return result(updated, changed.value, "updated");
569
- });
570
- }
571
790
  async readDatasetById(datasetId) {
572
791
  requireNonEmpty(datasetId, "datasetId");
573
792
  const record = await this.options.registry.get(datasetId);
@@ -642,6 +861,10 @@ export class SharedBackupController {
642
861
  }
643
862
  return implementation;
644
863
  }
864
+ codecForDataset(datasetId) {
865
+ return this.options.codecForDataset?.(datasetId) ??
866
+ this.options.codec;
867
+ }
645
868
  cryptoOptions() {
646
869
  return {
647
870
  crypto: this.crypto(),