@peers-app/peers-sdk 0.27.5 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/context/user-context.d.ts +7 -1
  2. package/dist/context/user-context.js +4 -9
  3. package/dist/contracts/contract-proxy.d.ts +5 -0
  4. package/dist/contracts/contract-proxy.js +2 -2
  5. package/dist/contracts/contract-resolvers.d.ts +2 -2
  6. package/dist/contracts/contract-resolvers.js +14 -15
  7. package/dist/contracts/system/network-contracts.d.ts +1 -1
  8. package/dist/contracts/tool-catalog-service.js +1 -1
  9. package/dist/data/assistants.d.ts +3 -3
  10. package/dist/data/authority-write-checks.d.ts +42 -0
  11. package/dist/data/authority-write-checks.js +324 -0
  12. package/dist/data/change-tracking.d.ts +4 -4
  13. package/dist/data/create-group.js +1 -0
  14. package/dist/data/devices.d.ts +27 -4
  15. package/dist/data/devices.js +31 -0
  16. package/dist/data/group-authority.d.ts +111 -0
  17. package/dist/data/group-authority.js +281 -0
  18. package/dist/data/group-members.d.ts +8 -2
  19. package/dist/data/group-members.js +41 -18
  20. package/dist/data/group-permissions.d.ts +15 -31
  21. package/dist/data/group-permissions.js +25 -95
  22. package/dist/data/group-resign.d.ts +38 -0
  23. package/dist/data/group-resign.js +71 -0
  24. package/dist/data/groups.d.ts +11 -0
  25. package/dist/data/groups.js +31 -13
  26. package/dist/data/index.d.ts +3 -0
  27. package/dist/data/index.js +3 -0
  28. package/dist/data/invites.d.ts +14 -0
  29. package/dist/data/orm/index.d.ts +1 -0
  30. package/dist/data/orm/index.js +1 -0
  31. package/dist/data/orm/sql.data-source.d.ts +2 -2
  32. package/dist/data/orm/sql.data-source.js +24 -22
  33. package/dist/data/orm/table-container.js +20 -11
  34. package/dist/data/orm/write-checks.d.ts +89 -0
  35. package/dist/data/orm/write-checks.js +115 -0
  36. package/dist/data/package-table-identity.d.ts +21 -0
  37. package/dist/data/package-table-identity.js +87 -0
  38. package/dist/data/package-version-resolver.d.ts +15 -0
  39. package/dist/data/package-version-resolver.js +70 -3
  40. package/dist/data/package-versions.d.ts +20 -14
  41. package/dist/data/package-versions.js +15 -9
  42. package/dist/data/packages.d.ts +6 -1
  43. package/dist/data/packages.js +15 -20
  44. package/dist/data/persistent-vars.js +2 -39
  45. package/dist/data/sync-recovery-queue.d.ts +4 -4
  46. package/dist/data/sync-recovery-queue.js +7 -1
  47. package/dist/data/tool-access-approvals.d.ts +3 -3
  48. package/dist/data/user-permissions.d.ts +33 -2
  49. package/dist/data/user-permissions.js +87 -10
  50. package/dist/data/users.d.ts +49 -12
  51. package/dist/data/users.js +21 -1
  52. package/dist/data/workflows.d.ts +1 -1
  53. package/dist/define/index.d.ts +1 -1
  54. package/dist/define/index.js +2 -1
  55. package/dist/define/manifest.js +9 -1
  56. package/dist/define/table.d.ts +15 -0
  57. package/dist/device/binary-peer-connection-v2.d.ts +1 -1
  58. package/dist/device/binary-peer-connection-v2.js +1 -1
  59. package/dist/device/connection.d.ts +1 -1
  60. package/dist/device/frame-cipher.d.ts +1 -1
  61. package/dist/device/frame-cipher.js +1 -1
  62. package/dist/device/get-trust-level-fn.js +3 -6
  63. package/dist/device-pairing/types.d.ts +26 -26
  64. package/dist/identity/user-row-change.d.ts +3 -1
  65. package/dist/identity/user-row-change.js +33 -18
  66. package/dist/invites/group-approval.js +23 -24
  67. package/dist/invites/invite-messages.d.ts +250 -28
  68. package/dist/invites/invite-token.d.ts +18 -0
  69. package/dist/invites/invite-token.js +2 -0
  70. package/dist/keys.d.ts +7 -0
  71. package/dist/keys.js +27 -0
  72. package/dist/package-installer/package-installer.d.ts +1 -1
  73. package/dist/package-installer/package-installer.js +8 -3
  74. package/dist/package-installer/package-propagation.d.ts +2 -0
  75. package/dist/package-installer/package-propagation.js +20 -38
  76. package/dist/package-installer/package-remote-checker.js +15 -3
  77. package/dist/package-loader/isolated-artifact.d.ts +14 -0
  78. package/dist/package-loader/isolated-artifact.js +3 -3
  79. package/dist/package-loader/isolated-runtime.d.ts +3 -1
  80. package/dist/package-loader/isolated-table-gateway.js +4 -1
  81. package/dist/package-loader/package-loader.js +6 -0
  82. package/dist/peer-id.d.ts +19 -0
  83. package/dist/peer-id.js +41 -0
  84. package/dist/types/peer-device.d.ts +2 -0
  85. package/dist/types/workflow.d.ts +2 -2
  86. package/dist/utils.js +13 -32
  87. package/package.json +1 -1
  88. package/dist/data/package-permissions.d.ts +0 -14
  89. package/dist/data/package-permissions.js +0 -26
  90. package/dist/data/package-version-permissions.d.ts +0 -14
  91. package/dist/data/package-version-permissions.js +0 -30
@@ -1142,15 +1142,15 @@ export declare const devicePairingTranscriptSchema: z.ZodObject<{
1142
1142
  }, "strip", z.ZodTypeAny, {
1143
1143
  publicKey: string;
1144
1144
  userId: string;
1145
- deviceId: string;
1146
1145
  publicBoxKey: string;
1146
+ deviceId: string;
1147
1147
  userName?: string | undefined;
1148
1148
  deviceName?: string | undefined;
1149
1149
  }, {
1150
1150
  publicKey: string;
1151
1151
  userId: string;
1152
- deviceId: string;
1153
1152
  publicBoxKey: string;
1153
+ deviceId: string;
1154
1154
  userName?: string | undefined;
1155
1155
  deviceName?: string | undefined;
1156
1156
  }>;
@@ -1164,15 +1164,15 @@ export declare const devicePairingTranscriptSchema: z.ZodObject<{
1164
1164
  }, "strip", z.ZodTypeAny, {
1165
1165
  publicKey: string;
1166
1166
  userId: string;
1167
- deviceId: string;
1168
1167
  publicBoxKey: string;
1168
+ deviceId: string;
1169
1169
  userName?: string | undefined;
1170
1170
  deviceName?: string | undefined;
1171
1171
  }, {
1172
1172
  publicKey: string;
1173
1173
  userId: string;
1174
- deviceId: string;
1175
1174
  publicBoxKey: string;
1175
+ deviceId: string;
1176
1176
  userName?: string | undefined;
1177
1177
  deviceName?: string | undefined;
1178
1178
  }>;
@@ -1188,16 +1188,16 @@ export declare const devicePairingTranscriptSchema: z.ZodObject<{
1188
1188
  sourceIdentity: {
1189
1189
  publicKey: string;
1190
1190
  userId: string;
1191
- deviceId: string;
1192
1191
  publicBoxKey: string;
1192
+ deviceId: string;
1193
1193
  userName?: string | undefined;
1194
1194
  deviceName?: string | undefined;
1195
1195
  };
1196
1196
  destinationIdentity: {
1197
1197
  publicKey: string;
1198
1198
  userId: string;
1199
- deviceId: string;
1200
1199
  publicBoxKey: string;
1200
+ deviceId: string;
1201
1201
  userName?: string | undefined;
1202
1202
  deviceName?: string | undefined;
1203
1203
  };
@@ -1212,16 +1212,16 @@ export declare const devicePairingTranscriptSchema: z.ZodObject<{
1212
1212
  sourceIdentity: {
1213
1213
  publicKey: string;
1214
1214
  userId: string;
1215
- deviceId: string;
1216
1215
  publicBoxKey: string;
1216
+ deviceId: string;
1217
1217
  userName?: string | undefined;
1218
1218
  deviceName?: string | undefined;
1219
1219
  };
1220
1220
  destinationIdentity: {
1221
1221
  publicKey: string;
1222
1222
  userId: string;
1223
- deviceId: string;
1224
1223
  publicBoxKey: string;
1224
+ deviceId: string;
1225
1225
  userName?: string | undefined;
1226
1226
  deviceName?: string | undefined;
1227
1227
  };
@@ -1262,15 +1262,15 @@ export declare const pairingConfirmationContentsSchema: z.ZodObject<{
1262
1262
  }, "strip", z.ZodTypeAny, {
1263
1263
  publicKey: string;
1264
1264
  userId: string;
1265
- deviceId: string;
1266
1265
  publicBoxKey: string;
1266
+ deviceId: string;
1267
1267
  userName?: string | undefined;
1268
1268
  deviceName?: string | undefined;
1269
1269
  }, {
1270
1270
  publicKey: string;
1271
1271
  userId: string;
1272
- deviceId: string;
1273
1272
  publicBoxKey: string;
1273
+ deviceId: string;
1274
1274
  userName?: string | undefined;
1275
1275
  deviceName?: string | undefined;
1276
1276
  }>;
@@ -1293,8 +1293,8 @@ export declare const pairingConfirmationContentsSchema: z.ZodObject<{
1293
1293
  device: {
1294
1294
  publicKey: string;
1295
1295
  userId: string;
1296
- deviceId: string;
1297
1296
  publicBoxKey: string;
1297
+ deviceId: string;
1298
1298
  userName?: string | undefined;
1299
1299
  deviceName?: string | undefined;
1300
1300
  };
@@ -1310,8 +1310,8 @@ export declare const pairingConfirmationContentsSchema: z.ZodObject<{
1310
1310
  device: {
1311
1311
  publicKey: string;
1312
1312
  userId: string;
1313
- deviceId: string;
1314
1313
  publicBoxKey: string;
1314
+ deviceId: string;
1315
1315
  userName?: string | undefined;
1316
1316
  deviceName?: string | undefined;
1317
1317
  };
@@ -1342,15 +1342,15 @@ export declare const pairingConfirmationSchema: z.ZodObject<{
1342
1342
  }, "strip", z.ZodTypeAny, {
1343
1343
  publicKey: string;
1344
1344
  userId: string;
1345
- deviceId: string;
1346
1345
  publicBoxKey: string;
1346
+ deviceId: string;
1347
1347
  userName?: string | undefined;
1348
1348
  deviceName?: string | undefined;
1349
1349
  }, {
1350
1350
  publicKey: string;
1351
1351
  userId: string;
1352
- deviceId: string;
1353
1352
  publicBoxKey: string;
1353
+ deviceId: string;
1354
1354
  userName?: string | undefined;
1355
1355
  deviceName?: string | undefined;
1356
1356
  }>;
@@ -1375,8 +1375,8 @@ export declare const pairingConfirmationSchema: z.ZodObject<{
1375
1375
  device: {
1376
1376
  publicKey: string;
1377
1377
  userId: string;
1378
- deviceId: string;
1379
1378
  publicBoxKey: string;
1379
+ deviceId: string;
1380
1380
  userName?: string | undefined;
1381
1381
  deviceName?: string | undefined;
1382
1382
  };
@@ -1393,8 +1393,8 @@ export declare const pairingConfirmationSchema: z.ZodObject<{
1393
1393
  device: {
1394
1394
  publicKey: string;
1395
1395
  userId: string;
1396
- deviceId: string;
1397
1396
  publicBoxKey: string;
1397
+ deviceId: string;
1398
1398
  userName?: string | undefined;
1399
1399
  deviceName?: string | undefined;
1400
1400
  };
@@ -1460,15 +1460,15 @@ export declare const pairingInstallationReceiptContentsSchema: z.ZodObject<{
1460
1460
  }, "strip", z.ZodTypeAny, {
1461
1461
  publicKey: string;
1462
1462
  userId: string;
1463
- deviceId: string;
1464
1463
  publicBoxKey: string;
1464
+ deviceId: string;
1465
1465
  userName?: string | undefined;
1466
1466
  deviceName?: string | undefined;
1467
1467
  }, {
1468
1468
  publicKey: string;
1469
1469
  userId: string;
1470
- deviceId: string;
1471
1470
  publicBoxKey: string;
1471
+ deviceId: string;
1472
1472
  userName?: string | undefined;
1473
1473
  deviceName?: string | undefined;
1474
1474
  }>;
@@ -1479,8 +1479,8 @@ export declare const pairingInstallationReceiptContentsSchema: z.ZodObject<{
1479
1479
  initializedDevice: {
1480
1480
  publicKey: string;
1481
1481
  userId: string;
1482
- deviceId: string;
1483
1482
  publicBoxKey: string;
1483
+ deviceId: string;
1484
1484
  userName?: string | undefined;
1485
1485
  deviceName?: string | undefined;
1486
1486
  };
@@ -1491,8 +1491,8 @@ export declare const pairingInstallationReceiptContentsSchema: z.ZodObject<{
1491
1491
  initializedDevice: {
1492
1492
  publicKey: string;
1493
1493
  userId: string;
1494
- deviceId: string;
1495
1494
  publicBoxKey: string;
1495
+ deviceId: string;
1496
1496
  userName?: string | undefined;
1497
1497
  deviceName?: string | undefined;
1498
1498
  };
@@ -1515,15 +1515,15 @@ export declare const signedPairingInstallationReceiptSchema: z.ZodObject<{
1515
1515
  }, "strip", z.ZodTypeAny, {
1516
1516
  publicKey: string;
1517
1517
  userId: string;
1518
- deviceId: string;
1519
1518
  publicBoxKey: string;
1519
+ deviceId: string;
1520
1520
  userName?: string | undefined;
1521
1521
  deviceName?: string | undefined;
1522
1522
  }, {
1523
1523
  publicKey: string;
1524
1524
  userId: string;
1525
- deviceId: string;
1526
1525
  publicBoxKey: string;
1526
+ deviceId: string;
1527
1527
  userName?: string | undefined;
1528
1528
  deviceName?: string | undefined;
1529
1529
  }>;
@@ -1534,8 +1534,8 @@ export declare const signedPairingInstallationReceiptSchema: z.ZodObject<{
1534
1534
  initializedDevice: {
1535
1535
  publicKey: string;
1536
1536
  userId: string;
1537
- deviceId: string;
1538
1537
  publicBoxKey: string;
1538
+ deviceId: string;
1539
1539
  userName?: string | undefined;
1540
1540
  deviceName?: string | undefined;
1541
1541
  };
@@ -1546,8 +1546,8 @@ export declare const signedPairingInstallationReceiptSchema: z.ZodObject<{
1546
1546
  initializedDevice: {
1547
1547
  publicKey: string;
1548
1548
  userId: string;
1549
- deviceId: string;
1550
1549
  publicBoxKey: string;
1550
+ deviceId: string;
1551
1551
  userName?: string | undefined;
1552
1552
  deviceName?: string | undefined;
1553
1553
  };
@@ -1564,8 +1564,8 @@ export declare const signedPairingInstallationReceiptSchema: z.ZodObject<{
1564
1564
  initializedDevice: {
1565
1565
  publicKey: string;
1566
1566
  userId: string;
1567
- deviceId: string;
1568
1567
  publicBoxKey: string;
1568
+ deviceId: string;
1569
1569
  userName?: string | undefined;
1570
1570
  deviceName?: string | undefined;
1571
1571
  };
@@ -1580,8 +1580,8 @@ export declare const signedPairingInstallationReceiptSchema: z.ZodObject<{
1580
1580
  initializedDevice: {
1581
1581
  publicKey: string;
1582
1582
  userId: string;
1583
- deviceId: string;
1584
1583
  publicBoxKey: string;
1584
+ deviceId: string;
1585
1585
  userName?: string | undefined;
1586
1586
  deviceName?: string | undefined;
1587
1587
  };
@@ -33,9 +33,11 @@ export interface ResolveUserRowChangeParams {
33
33
  * The record is the personal `Users` row when it exists, else the row already stored in the
34
34
  * table being written. The rule:
35
35
  *
36
- * - Nothing stored yet: the row is the bootstrap and is applied as it is.
36
+ * - Nothing stored yet: bootstrap the identity; its initial anchors need an owner signature.
37
37
  * - A different key: the anchors on record decide ({@link evaluatePresentedKey}). The
38
38
  * presented anchor list is ignored; the applied row keeps the list on record.
39
+ * - The user's own row with their current keys: takes the anchor list from their personal row.
40
+ * - A row with no list keeps the list on record.
39
41
  * - The same key and a different list: when nothing is on record the list is accepted as the
40
42
  * bootstrap (an upgraded contact announcing its first anchors); when the personal record
41
43
  * already holds the list it is accepted; otherwise the anchors on record must publish it
@@ -1,17 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveUserRowChange = resolveUserRowChange;
4
+ const user_permissions_1 = require("../data/user-permissions");
4
5
  const users_1 = require("../data/users");
5
6
  const anchor_resolution_1 = require("./anchor-resolution");
6
7
  const key_change_1 = require("./key-change");
7
- function withAnchors(row, anchors) {
8
- if ((0, anchor_resolution_1.sameAnchorList)(row.anchors, anchors))
9
- return row;
10
- if (!anchors?.length) {
11
- const { anchors: _dropped, ...rest } = row;
12
- return rest;
13
- }
14
- return { ...row, anchors };
8
+ /** `row` carrying `source`'s anchor list and the owner's signature over it, or no list. */
9
+ function withListOf(row, source) {
10
+ const { anchors: _anchors, anchorsSignature: _signature, ...profile } = row;
11
+ return { ...profile, ...(0, user_permissions_1.anchorFields)(source) };
12
+ }
13
+ /** `row` as a first record: its list only when the owner signed it, else no list. */
14
+ function bootstrap(row) {
15
+ return (0, user_permissions_1.hasSignedUserAnchors)(row) ? row : withListOf(row, {});
15
16
  }
16
17
  /**
17
18
  * The one decision for every path that writes another user's `Users` row: a direct save, a
@@ -20,9 +21,11 @@ function withAnchors(row, anchors) {
20
21
  * The record is the personal `Users` row when it exists, else the row already stored in the
21
22
  * table being written. The rule:
22
23
  *
23
- * - Nothing stored yet: the row is the bootstrap and is applied as it is.
24
+ * - Nothing stored yet: bootstrap the identity; its initial anchors need an owner signature.
24
25
  * - A different key: the anchors on record decide ({@link evaluatePresentedKey}). The
25
26
  * presented anchor list is ignored; the applied row keeps the list on record.
27
+ * - The user's own row with their current keys: takes the anchor list from their personal row.
28
+ * - A row with no list keeps the list on record.
26
29
  * - The same key and a different list: when nothing is on record the list is accepted as the
27
30
  * bootstrap (an upgraded contact announcing its first anchors); when the personal record
28
31
  * already holds the list it is accepted; otherwise the anchors on record must publish it
@@ -31,12 +34,19 @@ function withAnchors(row, anchors) {
31
34
  */
32
35
  async function resolveUserRowChange(params) {
33
36
  const { existing, incoming, personalContext } = params;
34
- if (!existing?.publicKey)
35
- return { action: "apply", row: incoming };
36
37
  const personal = await (0, users_1.Users)(personalContext).get(incoming.userId);
37
38
  const record = personal?.publicKey ? personal : existing;
38
- const recordAnchors = personal?.anchors?.length ? personal.anchors : (existing.anchors ?? []);
39
- if (incoming.publicKey !== existing.publicKey) {
39
+ if (!record?.publicKey)
40
+ return { action: "apply", row: bootstrap(incoming) };
41
+ const recordAnchors = record.anchors ?? [];
42
+ // My own row with my current keys: my personal copy is the record of my anchors, so a copy
43
+ // anywhere else carries that list (a stale or replayed copy can't move it either way).
44
+ if (incoming.userId === personalContext.userContext.userId &&
45
+ personal?.publicKey === incoming.publicKey &&
46
+ personal.publicBoxKey === incoming.publicBoxKey) {
47
+ return { action: "apply", row: withListOf(incoming, personal) };
48
+ }
49
+ if (incoming.publicKey !== record.publicKey || incoming.publicBoxKey !== record.publicBoxKey) {
40
50
  const decision = await (0, key_change_1.evaluatePresentedKey)({
41
51
  dataContext: personalContext,
42
52
  userId: incoming.userId,
@@ -47,18 +57,23 @@ async function resolveUserRowChange(params) {
47
57
  if (decision.accepted) {
48
58
  // The resolver may have just stored a confirmed list on the personal row.
49
59
  const confirmed = await (0, users_1.Users)(personalContext).get(incoming.userId);
50
- const anchors = confirmed?.anchors?.length ? confirmed.anchors : recordAnchors;
51
- return { action: "apply", row: withAnchors(incoming, anchors) };
60
+ return {
61
+ action: "apply",
62
+ row: withListOf(incoming, confirmed?.anchors?.length ? confirmed : record),
63
+ };
52
64
  }
53
65
  if (decision.state === "pending" || decision.state === "recovery-pending") {
54
66
  return { action: "defer", reason: "Key change is not confirmed by the anchors on record" };
55
67
  }
56
68
  return { action: "refuse", reason: "Key change is not confirmed by the anchors on record" };
57
69
  }
58
- if ((0, anchor_resolution_1.sameAnchorList)(existing.anchors, incoming.anchors))
70
+ if ((0, anchor_resolution_1.sameAnchorList)(record.anchors, incoming.anchors))
59
71
  return { action: "apply", row: incoming };
72
+ // A row with no list (a stub, a copy that dropped it) proposes no change: keep the record's.
73
+ if (!incoming.anchors?.length)
74
+ return { action: "apply", row: withListOf(incoming, record) };
60
75
  if (recordAnchors.length === 0)
61
- return { action: "apply", row: incoming };
76
+ return { action: "apply", row: bootstrap(incoming) };
62
77
  if ((0, anchor_resolution_1.sameAnchorList)(personal?.anchors, incoming.anchors)) {
63
78
  return { action: "apply", row: incoming };
64
79
  }
@@ -76,6 +91,6 @@ async function resolveUserRowChange(params) {
76
91
  reason: "Anchor list change is not confirmed by the anchors on record",
77
92
  };
78
93
  }
79
- return { action: "apply", row: withAnchors(incoming, recordAnchors) };
94
+ return { action: "apply", row: withListOf(incoming, record) };
80
95
  }
81
96
  (0, users_1.setUserRowChangeGuard)((personalContext, incoming, existing) => resolveUserRowChange({ personalContext, incoming, existing }));
@@ -35,13 +35,15 @@ async function buildGroupApproval(userContext, input) {
35
35
  groupId: input.groupId,
36
36
  userId: userContext.userId,
37
37
  role: data_1.GroupMemberRole.Founder,
38
+ publicKey: input.approverKeys.publicKey,
38
39
  signature: "",
39
40
  });
40
41
  }
41
- if (!approvingMember || approvingMember.deleted || approvingMember.role < data_1.GroupMemberRole.Admin) {
42
+ const approvingRole = await (0, data_1.getUserRole)(input.groupId, userContext.userId, userContext);
43
+ if (!approvingMember || approvingRole < data_1.GroupMemberRole.Admin) {
42
44
  throw new Error("Only Admin or higher members can approve group joins");
43
45
  }
44
- if (input.role > approvingMember.role) {
46
+ if (input.role > approvingRole) {
45
47
  throw new Error("Cannot grant a role higher than your own");
46
48
  }
47
49
  const groupSecretKey = await (0, data_1.readGroupSecretKey)(userContext, input.groupId, input.approverKeys.secretKey);
@@ -58,27 +60,24 @@ async function buildGroupApproval(userContext, input) {
58
60
  groupId: input.groupId,
59
61
  userId: input.joiner.userId,
60
62
  });
61
- let membership;
62
- if (existingMember) {
63
- existingMember.role = input.role;
64
- delete existingMember.deleted;
65
- membership = await groupMembersTable.signAndSave(existingMember);
66
- }
67
- else {
68
- // The same id on each of the approver's devices: a join several of them approve at once
69
- // is one row, not rows that collide on (groupId, userId).
70
- membership = await groupMembersTable.signAndSave({
71
- groupMemberId: (0, utils_1.deterministicGroupMemberId)(input.groupId, input.joiner.userId),
72
- groupId: input.groupId,
73
- userId: input.joiner.userId,
74
- role: input.role,
75
- signature: "",
76
- });
77
- }
78
- let adminMembers = await groupMembersTable.list({ deleted: { $ne: true }, role: { $gte: data_1.GroupMemberRole.Admin } }, { pageSize: 5, sortBy: ["-groupMemberId"] });
79
- if (!adminMembers.some((member) => member.groupMemberId === approvingMember.groupMemberId)) {
80
- adminMembers = [approvingMember, ...adminMembers];
81
- }
63
+ // The same id on each approving device, while preserving an existing row's identity.
64
+ const membership = await groupMembersTable.signAndSave({
65
+ groupMemberId: (0, utils_1.deterministicGroupMemberId)(input.groupId, input.joiner.userId),
66
+ signature: "",
67
+ ...existingMember,
68
+ groupId: input.groupId,
69
+ userId: input.joiner.userId,
70
+ role: input.role,
71
+ publicKey: input.joiner.publicKey,
72
+ deleted: undefined,
73
+ });
74
+ const authority = await (0, data_1.getGroupAuthority)(userContext, input.groupId);
75
+ const adminMembers = (await groupMembersTable.list({ groupId: input.groupId })).filter((member) => !member.deleted &&
76
+ member.role >= data_1.GroupMemberRole.Admin &&
77
+ authority.roles.get(member.userId) === member.role);
78
+ // A joiner must verify the complete grant chain before it can admit its first peer: the
79
+ // verified profile of each member whose key counts, and no other contact of the approver's.
80
+ const seedUsers = [...authority.keys.keys()].flatMap((userId) => authority.users.get(userId) ?? []);
82
81
  const me = await (0, data_1.Users)(userContext.userDataContext).get(userContext.userId);
83
82
  if (!me) {
84
83
  throw new Error("My user object was not found in the DB. Is the user setup?");
@@ -100,7 +99,7 @@ async function buildGroupApproval(userContext, input) {
100
99
  membership,
101
100
  seed: {
102
101
  groupMembers: adminMembers,
103
- users: [me],
102
+ users: seedUsers,
104
103
  devices: seedDevices,
105
104
  },
106
105
  approver: {