@peers-app/peers-sdk 0.27.5 → 0.28.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.
Files changed (81) 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 +8 -9
  33. package/dist/data/orm/table-container.js +14 -8
  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 +6 -0
  37. package/dist/data/package-table-identity.js +51 -0
  38. package/dist/data/package-version-resolver.d.ts +15 -0
  39. package/dist/data/package-version-resolver.js +58 -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/device/binary-peer-connection-v2.d.ts +1 -1
  54. package/dist/device/binary-peer-connection-v2.js +1 -1
  55. package/dist/device/connection.d.ts +1 -1
  56. package/dist/device/frame-cipher.d.ts +1 -1
  57. package/dist/device/frame-cipher.js +1 -1
  58. package/dist/device/get-trust-level-fn.js +3 -6
  59. package/dist/device-pairing/types.d.ts +26 -26
  60. package/dist/identity/user-row-change.d.ts +3 -1
  61. package/dist/identity/user-row-change.js +33 -18
  62. package/dist/invites/group-approval.js +23 -24
  63. package/dist/invites/invite-messages.d.ts +250 -28
  64. package/dist/invites/invite-token.d.ts +18 -0
  65. package/dist/invites/invite-token.js +2 -0
  66. package/dist/keys.d.ts +7 -0
  67. package/dist/keys.js +27 -0
  68. package/dist/package-installer/package-installer.d.ts +1 -1
  69. package/dist/package-installer/package-installer.js +8 -3
  70. package/dist/package-installer/package-propagation.d.ts +2 -0
  71. package/dist/package-installer/package-propagation.js +20 -38
  72. package/dist/package-installer/package-remote-checker.js +15 -3
  73. package/dist/package-loader/isolated-artifact.js +0 -2
  74. package/dist/package-loader/package-loader.js +6 -0
  75. package/dist/types/peer-device.d.ts +2 -0
  76. package/dist/types/workflow.d.ts +2 -2
  77. package/package.json +1 -1
  78. package/dist/data/package-permissions.d.ts +0 -14
  79. package/dist/data/package-permissions.js +0 -26
  80. package/dist/data/package-version-permissions.d.ts +0 -14
  81. package/dist/data/package-version-permissions.js +0 -30
@@ -35,6 +35,8 @@ export declare const inviteIdentitySchema: z.ZodObject<{
35
35
  type: "peers-services" | "https";
36
36
  url: string;
37
37
  }>, "many">>;
38
+ /** The owner's signature over that first anchor list (see `signUserObject`). */
39
+ anchorsSignature: z.ZodOptional<z.ZodString>;
38
40
  }, "strict", z.ZodTypeAny, {
39
41
  publicKey: string;
40
42
  userId: string;
@@ -43,6 +45,7 @@ export declare const inviteIdentitySchema: z.ZodObject<{
43
45
  type: "peers-services" | "https";
44
46
  url: string;
45
47
  }[] | undefined;
48
+ anchorsSignature?: string | undefined;
46
49
  displayName?: string | undefined;
47
50
  }, {
48
51
  publicKey: string;
@@ -52,6 +55,7 @@ export declare const inviteIdentitySchema: z.ZodObject<{
52
55
  type: "peers-services" | "https";
53
56
  url: string;
54
57
  }[] | undefined;
58
+ anchorsSignature?: string | undefined;
55
59
  displayName?: string | undefined;
56
60
  }>;
57
61
  /** Public identity of a party to an invite. */
@@ -110,6 +114,8 @@ export declare const peersInviteContentsSchema: z.ZodEffects<z.ZodObject<{
110
114
  type: "peers-services" | "https";
111
115
  url: string;
112
116
  }>, "many">>;
117
+ /** The owner's signature over that first anchor list (see `signUserObject`). */
118
+ anchorsSignature: z.ZodOptional<z.ZodString>;
113
119
  }, "strict", z.ZodTypeAny, {
114
120
  publicKey: string;
115
121
  userId: string;
@@ -118,6 +124,7 @@ export declare const peersInviteContentsSchema: z.ZodEffects<z.ZodObject<{
118
124
  type: "peers-services" | "https";
119
125
  url: string;
120
126
  }[] | undefined;
127
+ anchorsSignature?: string | undefined;
121
128
  displayName?: string | undefined;
122
129
  }, {
123
130
  publicKey: string;
@@ -127,6 +134,7 @@ export declare const peersInviteContentsSchema: z.ZodEffects<z.ZodObject<{
127
134
  type: "peers-services" | "https";
128
135
  url: string;
129
136
  }[] | undefined;
137
+ anchorsSignature?: string | undefined;
130
138
  displayName?: string | undefined;
131
139
  }>;
132
140
  /** ISO 8601 instant after which the token is invalid. */
@@ -171,6 +179,7 @@ export declare const peersInviteContentsSchema: z.ZodEffects<z.ZodObject<{
171
179
  type: "peers-services" | "https";
172
180
  url: string;
173
181
  }[] | undefined;
182
+ anchorsSignature?: string | undefined;
174
183
  displayName?: string | undefined;
175
184
  };
176
185
  epoch: number;
@@ -196,6 +205,7 @@ export declare const peersInviteContentsSchema: z.ZodEffects<z.ZodObject<{
196
205
  type: "peers-services" | "https";
197
206
  url: string;
198
207
  }[] | undefined;
208
+ anchorsSignature?: string | undefined;
199
209
  displayName?: string | undefined;
200
210
  };
201
211
  epoch: number;
@@ -221,6 +231,7 @@ export declare const peersInviteContentsSchema: z.ZodEffects<z.ZodObject<{
221
231
  type: "peers-services" | "https";
222
232
  url: string;
223
233
  }[] | undefined;
234
+ anchorsSignature?: string | undefined;
224
235
  displayName?: string | undefined;
225
236
  };
226
237
  epoch: number;
@@ -246,6 +257,7 @@ export declare const peersInviteContentsSchema: z.ZodEffects<z.ZodObject<{
246
257
  type: "peers-services" | "https";
247
258
  url: string;
248
259
  }[] | undefined;
260
+ anchorsSignature?: string | undefined;
249
261
  displayName?: string | undefined;
250
262
  };
251
263
  epoch: number;
@@ -284,6 +296,8 @@ export declare const peersInviteSchema: z.ZodObject<{
284
296
  type: "peers-services" | "https";
285
297
  url: string;
286
298
  }>, "many">>;
299
+ /** The owner's signature over that first anchor list (see `signUserObject`). */
300
+ anchorsSignature: z.ZodOptional<z.ZodString>;
287
301
  }, "strict", z.ZodTypeAny, {
288
302
  publicKey: string;
289
303
  userId: string;
@@ -292,6 +306,7 @@ export declare const peersInviteSchema: z.ZodObject<{
292
306
  type: "peers-services" | "https";
293
307
  url: string;
294
308
  }[] | undefined;
309
+ anchorsSignature?: string | undefined;
295
310
  displayName?: string | undefined;
296
311
  }, {
297
312
  publicKey: string;
@@ -301,6 +316,7 @@ export declare const peersInviteSchema: z.ZodObject<{
301
316
  type: "peers-services" | "https";
302
317
  url: string;
303
318
  }[] | undefined;
319
+ anchorsSignature?: string | undefined;
304
320
  displayName?: string | undefined;
305
321
  }>;
306
322
  expiresAt: z.ZodString;
@@ -344,6 +360,7 @@ export declare const peersInviteSchema: z.ZodObject<{
344
360
  type: "peers-services" | "https";
345
361
  url: string;
346
362
  }[] | undefined;
363
+ anchorsSignature?: string | undefined;
347
364
  displayName?: string | undefined;
348
365
  };
349
366
  epoch: number;
@@ -370,6 +387,7 @@ export declare const peersInviteSchema: z.ZodObject<{
370
387
  type: "peers-services" | "https";
371
388
  url: string;
372
389
  }[] | undefined;
390
+ anchorsSignature?: string | undefined;
373
391
  displayName?: string | undefined;
374
392
  };
375
393
  epoch: number;
@@ -44,6 +44,8 @@ exports.inviteIdentitySchema = zod_1.z
44
44
  * keeps it; this is only the bootstrap that arrives with the invitation.
45
45
  */
46
46
  anchors: zod_1.z.array(anchor_document_1.anchorRefSchema).max(8).optional(),
47
+ /** The owner's signature over that first anchor list (see `signUserObject`). */
48
+ anchorsSignature: zod_1.z.string().max(200).optional(),
47
49
  })
48
50
  .strict();
49
51
  /** How joiners are admitted by a group token. */
package/dist/keys.d.ts CHANGED
@@ -138,6 +138,13 @@ export declare function addSignatureToObject<T extends Record<string, any>>(obj:
138
138
  export declare function verifyObjectSignature<T extends IMaybeSignature>(objectWithSignature: T): void;
139
139
  /** True if {@link verifyObjectSignature} would succeed. */
140
140
  export declare function isObjectSignatureValid<T extends IMaybeSignature>(objectWithSignature: T): boolean;
141
+ /**
142
+ * The key that validly signed `row` ({@link addSignatureToObject}'s `publicKey:signature`), or
143
+ * `undefined` when the row is unsigned or its signature doesn't verify. Memoized.
144
+ *
145
+ * @param row - A row with an optional `signature`.
146
+ */
147
+ export declare function signedByKey(row: IMaybeSignature): string | undefined;
141
148
  /**
142
149
  * Parses the Ed25519 public key prefix from a `publicKey:signature` field, or `undefined` if malformed.
143
150
  */
package/dist/keys.js CHANGED
@@ -27,6 +27,7 @@ exports.decryptData = decryptData;
27
27
  exports.addSignatureToObject = addSignatureToObject;
28
28
  exports.verifyObjectSignature = verifyObjectSignature;
29
29
  exports.isObjectSignatureValid = isObjectSignatureValid;
30
+ exports.signedByKey = signedByKey;
30
31
  exports.getPublicKeyFromObjectSignature = getPublicKeyFromObjectSignature;
31
32
  const sha2_1 = require("@noble/hashes/sha2");
32
33
  const buffer_1 = require("buffer");
@@ -391,6 +392,32 @@ function isObjectSignatureValid(objectWithSignature) {
391
392
  }
392
393
  return true;
393
394
  }
395
+ /**
396
+ * Signature checks already made, by the hash of the signed row (signature included). A group's
397
+ * authority is rebuilt after any change to its rows and re-checks every one, and an Ed25519
398
+ * check costs milliseconds, so without this a rebuild would take seconds in a large group.
399
+ */
400
+ const signatureChecks = new Map();
401
+ const SIGNATURE_CHECKS_MAX = 10_000;
402
+ /**
403
+ * The key that validly signed `row` ({@link addSignatureToObject}'s `publicKey:signature`), or
404
+ * `undefined` when the row is unsigned or its signature doesn't verify. Memoized.
405
+ *
406
+ * @param row - A row with an optional `signature`.
407
+ */
408
+ function signedByKey(row) {
409
+ if (!row.signature)
410
+ return undefined;
411
+ const key = hashValue(canonicalJSONString(row));
412
+ let valid = signatureChecks.get(key);
413
+ if (valid === undefined) {
414
+ valid = isObjectSignatureValid(row);
415
+ if (signatureChecks.size >= SIGNATURE_CHECKS_MAX)
416
+ signatureChecks.clear();
417
+ signatureChecks.set(key, valid);
418
+ }
419
+ return valid ? getPublicKeyFromObjectSignature(row) : undefined;
420
+ }
394
421
  /**
395
422
  * Parses the Ed25519 public key prefix from a `publicKey:signature` field, or `undefined` if malformed.
396
423
  */
@@ -10,7 +10,7 @@ import type { IInstallOpts, IInstallResult, IPackageInfo, IPackageInstallerDeps
10
10
  * triggers the package loader and device version resolution.
11
11
  *
12
12
  * When the bundle hash matches the existing PackageVersion, no files or PV are
13
- * rewritten. The Packages row still gets a missing `activePackageVersionId`
13
+ * rewritten. An Admin's Packages row still gets a missing `activePackageVersionId`
14
14
  * (and description), and when the id was missing the version is activated on
15
15
  * this device unless `skipResolve`.
16
16
  */
@@ -8,6 +8,8 @@ exports.getPackageInfo = getPackageInfo;
8
8
  exports.getPackageLocalPath = getPackageLocalPath;
9
9
  const file_types_1 = require("../data/files/file.types");
10
10
  const files_1 = require("../data/files/files");
11
+ const group_member_roles_1 = require("../data/group-member-roles");
12
+ const group_permissions_1 = require("../data/group-permissions");
11
13
  const package_version_resolver_1 = require("../data/package-version-resolver");
12
14
  const package_versions_1 = require("../data/package-versions");
13
15
  const packages_1 = require("../data/packages");
@@ -23,7 +25,7 @@ const package_author_signer_1 = require("./package-author-signer");
23
25
  * triggers the package loader and device version resolution.
24
26
  *
25
27
  * When the bundle hash matches the existing PackageVersion, no files or PV are
26
- * rewritten. The Packages row still gets a missing `activePackageVersionId`
28
+ * rewritten. An Admin's Packages row still gets a missing `activePackageVersionId`
27
29
  * (and description), and when the id was missing the version is activated on
28
30
  * this device unless `skipResolve`.
29
31
  */
@@ -127,7 +129,8 @@ async function installPackageFromBundles(dataContext, deps, packageId, opts) {
127
129
  * this device.
128
130
  *
129
131
  * Updates the description when it differs, and sets `activePackageVersionId`
130
- * when it is empty (first install, or reimport after the row was deleted).
132
+ * when it is empty and the current user may select for the group (first install, or
133
+ * reimport after the row was deleted). Writers activate dev builds on their own device.
131
134
  * Unless `skipResolve`, writes device prefs and force-resolves the version.
132
135
  * With `resolveOnlyWhenActivated`, the device step runs only when this call
133
136
  * filled an empty `activePackageVersionId`.
@@ -143,7 +146,9 @@ async function activateInstalledPackageVersion(dataContext, packageId, packageVe
143
146
  needsSave = true;
144
147
  }
145
148
  const activated = !pkg.activePackageVersionId;
146
- if (activated) {
149
+ const canSelectForGroup = !dataContext.groupId ||
150
+ (await (0, group_permissions_1.getUserRole)(dataContext.groupId, dataContext.userContext.userId, dataContext.userContext)) >= group_member_roles_1.GroupMemberRole.Admin;
151
+ if (activated && canSelectForGroup) {
147
152
  pkg.activePackageVersionId = packageVersion.packageVersionId;
148
153
  needsSave = true;
149
154
  }
@@ -22,8 +22,10 @@ export declare function propagatePvOnChanged(userContext?: UserContext): Promise
22
22
  * any that are missing from admin groups. Useful for manual repair.
23
23
  */
24
24
  export declare function discoverAndPropagateVersions(userContext: UserContext): Promise<IPropagationResult[]>;
25
+ /** Whether automatic follow policy selected a group release. */
25
26
  export interface IActivationResult {
26
27
  activated: boolean;
27
28
  activePackageVersionId?: string;
28
29
  }
30
+ /** Select the best publisher-signed release under an Admin-approved package's follow policy. */
29
31
  export declare function activateBestEligibleVersion(dataContext: DataContext, pkg: IPackage): Promise<IActivationResult>;
@@ -4,6 +4,7 @@ exports.propagatePvOnChanged = propagatePvOnChanged;
4
4
  exports.discoverAndPropagateVersions = discoverAndPropagateVersions;
5
5
  exports.activateBestEligibleVersion = activateBestEligibleVersion;
6
6
  const context_1 = require("../context");
7
+ const authority_write_checks_1 = require("../data/authority-write-checks");
7
8
  const files_1 = require("../data/files/files");
8
9
  const group_member_roles_1 = require("../data/group-member-roles");
9
10
  const group_permissions_1 = require("../data/group-permissions");
@@ -113,12 +114,12 @@ async function propagatePvToAdminGroups(userContext, sourceContext, pv) {
113
114
  */
114
115
  async function installPvInGroupNoActivate(source, dest, pv) {
115
116
  const pkg = await (0, packages_1.Packages)(dest).get(pv.packageId);
116
- if (!pkg || pkg.disabled)
117
+ if (!pkg || pkg.disabled || !(await (0, authority_write_checks_1.isPackageRowAdminSigned)(pkg, dest)))
117
118
  return false;
118
119
  const existingPv = await (0, package_versions_1.PackageVersions)(dest).get(pv.packageVersionId);
119
120
  if (existingPv)
120
121
  return false;
121
- if (!(await verifyOrEstablishTofu(source, dest, pkg, pv)))
122
+ if (!(await verifyPinnedPublisher(dest, pkg, pv)))
122
123
  return false;
123
124
  const files = await copyBundleFiles(source, pv);
124
125
  if (!files)
@@ -143,7 +144,8 @@ async function installPvInGroup(source, dest, pv, retryCnt = 0) {
143
144
  await (0, utils_1.sleep)(delayMs);
144
145
  }
145
146
  const pkg = await (0, packages_1.Packages)(dest).get(pv.packageId);
146
- if (!pkg || pkg.disabled)
147
+ // Copying signs the release with this device's key: only into a package an Admin added.
148
+ if (!pkg || pkg.disabled || !(await (0, authority_write_checks_1.isPackageRowAdminSigned)(pkg, dest)))
147
149
  return undefined;
148
150
  const existingPv = await (0, package_versions_1.PackageVersions)(dest).get(pv.packageVersionId);
149
151
  if (existingPv) {
@@ -153,10 +155,8 @@ async function installPvInGroup(source, dest, pv, retryCnt = 0) {
153
155
  ? makeResult(dest, pv, activation.activePackageVersionId === pv.packageVersionId)
154
156
  : undefined;
155
157
  }
156
- // TOFU verification
157
- if (!(await verifyOrEstablishTofu(source, dest, pkg, pv))) {
158
- return await retryOrFail(source, dest, pv, retryCnt, "TOFU");
159
- }
158
+ if (!(await verifyPinnedPublisher(dest, pkg, pv)))
159
+ return undefined;
160
160
  // Copy bundle files
161
161
  const files = await copyBundleFiles(source, pv);
162
162
  if (!files) {
@@ -184,45 +184,19 @@ async function retryOrFail(source, dest, pv, retryCnt, reason) {
184
184
  console.error(`[propagation] ${reason} for ${pv.packageVersionId}, giving up`);
185
185
  return undefined;
186
186
  }
187
- // --- TOFU ---
188
187
  /**
189
- * Returns true if signature verification passes. Establishes TOFU key
190
- * from source if target has no publishPublicKey yet. Returns false on
191
- * transient failures (missing source key).
188
+ * Another context can supply bytes, never a new trust root. The destination must already
189
+ * pin the publisher (or use the host's peers-core anchor) before this device endorses a release.
192
190
  */
193
- async function verifyOrEstablishTofu(sourceContext, targetContext, targetPkg, pv) {
191
+ async function verifyPinnedPublisher(targetContext, targetPkg, pv) {
194
192
  // peers-core pinned to a revoked key: move the pin to the host's current key first so a
195
193
  // version signed by the replacement key can be accepted.
196
194
  const repinned = await (0, peers_core_trust_anchor_1.repinPeersCoreIfRevoked)(targetContext, targetPkg);
197
195
  if (repinned !== targetPkg) {
198
196
  targetPkg.publishPublicKey = repinned.publishPublicKey;
199
197
  }
200
- if (targetPkg.publishPublicKey) {
201
- if (!(0, package_author_signing_1.verifyPackageAuthorSignature)(pv, targetPkg.publishPublicKey).valid) {
202
- console.error(`[propagation] Invalid signature for ${pv.packageVersionId}`);
203
- return false;
204
- }
205
- return true;
206
- }
207
- // TOFU: get key from source
208
- const sourcePkg = await (0, packages_1.Packages)(sourceContext).get(targetPkg.packageId);
209
- if (!sourcePkg?.publishPublicKey)
210
- return false;
211
- // Never adopt a revoked key as a trust anchor, however it reached the source group.
212
- if ((0, package_author_signing_1.isRevokedPublisherKey)(sourcePkg.publishPublicKey)) {
213
- console.error(`[propagation] Refusing revoked publisher key for ${targetPkg.packageId} from ${sourceContext.dataContextId}`);
214
- return false;
215
- }
216
- if (!(0, package_author_signing_1.verifyPackageAuthorSignature)(pv, sourcePkg.publishPublicKey).valid) {
217
- console.error(`[propagation] Invalid signature for ${pv.packageVersionId} (TOFU)`);
218
- return false;
219
- }
220
- await (0, packages_1.Packages)(targetContext).signAndSave({
221
- ...targetPkg,
222
- publishPublicKey: sourcePkg.publishPublicKey,
223
- });
224
- targetPkg.publishPublicKey = sourcePkg.publishPublicKey;
225
- return true;
198
+ return (!!targetPkg.publishPublicKey &&
199
+ (0, package_author_signing_1.verifyPackageAuthorSignature)(pv, targetPkg.publishPublicKey).valid);
226
200
  }
227
201
  /**
228
202
  * Loads and verifies all bundle file records from source. Returns null if
@@ -272,7 +246,15 @@ async function saveFileAndIndexTree(sourceFiles, destFiles, file, copiedIds) {
272
246
  }
273
247
  await destFiles.saveFileRecord(file);
274
248
  }
249
+ /** Select the best publisher-signed release under an Admin-approved package's follow policy. */
275
250
  async function activateBestEligibleVersion(dataContext, pkg) {
251
+ // Callers may have copied files or waited for an activation lock since reading the row.
252
+ // Always decide from current, approved settings before signing a group selection.
253
+ const current = await (0, packages_1.Packages)(dataContext).get(pkg.packageId);
254
+ if (!current || current.disabled || !(await (0, authority_write_checks_1.isPackageRowAdminSigned)(current, dataContext))) {
255
+ return { activated: false };
256
+ }
257
+ pkg = current;
276
258
  const range = pkg.versionFollowRange ?? "pinned";
277
259
  if (range === "pinned")
278
260
  return { activated: false };
@@ -10,6 +10,7 @@ exports.checkPackageRemoteForNewVersion = checkPackageRemoteForNewVersion;
10
10
  exports.checkAndInstallPackageRemoteVersion = checkAndInstallPackageRemoteVersion;
11
11
  exports.installRemotePackageVersion = installRemotePackageVersion;
12
12
  exports.clearRemoteCheckCache = clearRemoteCheckCache;
13
+ const authority_write_checks_1 = require("../data/authority-write-checks");
13
14
  const file_types_1 = require("../data/files/file.types");
14
15
  const package_version_resolver_1 = require("../data/package-version-resolver");
15
16
  const package_versions_1 = require("../data/package-versions");
@@ -45,12 +46,18 @@ function checkPackageRemoteForNewVersion(packageId, updateUrl, tag, http) {
45
46
  * group does not already have the remote PackageVersion.
46
47
  */
47
48
  async function checkAndInstallPackageRemoteVersion(dataContext, pkg, tag, http) {
48
- const updateUrl = pkg.updateUrl;
49
- if (!updateUrl || pkg.disabled)
49
+ let current = await (0, packages_1.Packages)(dataContext).get(pkg.packageId);
50
+ if (!current || current.disabled)
50
51
  return null;
51
52
  // A peers-core row pinned to a revoked publisher key would reject every new release; move
52
53
  // it to the host's current anchor before looking at the remote.
53
- await (0, peers_core_trust_anchor_1.repinPeersCoreIfRevoked)(dataContext, pkg);
54
+ current = await (0, peers_core_trust_anchor_1.repinPeersCoreIfRevoked)(dataContext, current);
55
+ // The release would be signed with this device's key: only for a package an Admin added.
56
+ if (!(await (0, authority_write_checks_1.isPackageRowAdminSigned)(current, dataContext)))
57
+ return null;
58
+ const updateUrl = current.updateUrl;
59
+ if (!updateUrl)
60
+ return null;
54
61
  const result = await checkPackageRemoteForNewVersion(pkg.packageId, updateUrl, tag, http);
55
62
  if (!result)
56
63
  return null;
@@ -139,6 +146,11 @@ async function installRemotePackageVersion(dataContext, result) {
139
146
  if (!pkg) {
140
147
  throw new Error(`Cannot install remote package version: package ${packageId} is not installed`);
141
148
  }
149
+ // The fetch may have outlived a delete/reinsert of the package. Never sign a release or
150
+ // establish a publisher pin on behalf of a newly added, unapproved row.
151
+ if (!(await (0, authority_write_checks_1.isPackageRowAdminSigned)(pkg, dataContext))) {
152
+ throw new Error("Cannot install remote package version: package settings need an Admin signature");
153
+ }
142
154
  if (!unpacked.payload.publicKey) {
143
155
  throw new Error("Cannot install remote package version: payload publicKey is missing");
144
156
  }
@@ -783,8 +783,6 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
783
783
  "tables",
784
784
  "network",
785
785
  "appNavs",
786
- // Voice Skill v1's retired action aliases: an older manifest may carry them; ignored.
787
- "actionAliases",
788
786
  "executionMode",
789
787
  "guestApi",
790
788
  ], "manifest");
@@ -751,9 +751,15 @@ class PackageLoader {
751
751
  /* no version record */
752
752
  }
753
753
  }
754
+ if (packageVersion) {
755
+ await (0, package_version_resolver_1.assertPackageVersionRunnable)(pkg, packageVersion, this.dataContext, !!opts?.localPath);
756
+ }
754
757
  if (bundleFileId) {
755
758
  const bundleBuffer = await (0, files_1.Files)(this.dataContext).getFileContents(bundleFileId);
756
759
  if (bundleBuffer) {
760
+ if (this.dataContext.groupId && packageVersion) {
761
+ (0, package_version_resolver_1.assertPackageBundleIntegrity)(packageVersion, "package", bundleBuffer);
762
+ }
757
763
  bundleCode = Buffer.from(bundleBuffer).toString("utf8");
758
764
  }
759
765
  else if (warn) {
@@ -7,6 +7,8 @@ export interface IPeerDevice {
7
7
  deviceId: string;
8
8
  /** User identity that owns this device. */
9
9
  userId: string;
10
+ /** Signing key the device authenticated with, for a remote device: roles are checked against it. */
11
+ publicKey?: string;
10
12
  /** Role used when this device participates in a shared group. */
11
13
  role: GroupMemberRole;
12
14
  /** Lists change records available from this device. */
@@ -45,6 +45,7 @@ export declare const workflowSchema: z.ZodObject<{
45
45
  name: string;
46
46
  description: string;
47
47
  updatedAt: Date;
48
+ createdBy: string;
48
49
  createdAt: Date;
49
50
  workflowId: string;
50
51
  defaultAssistantId: string;
@@ -54,14 +55,13 @@ export declare const workflowSchema: z.ZodObject<{
54
55
  directCallToolId?: string | undefined;
55
56
  subWorkflowId?: string | undefined;
56
57
  }[];
57
- createdBy: string;
58
58
  packageId?: string | undefined;
59
59
  }, {
60
60
  name: string;
61
61
  description: string;
62
+ createdBy: string;
62
63
  workflowId: string;
63
64
  defaultAssistantId: string;
64
- createdBy: string;
65
65
  updatedAt?: Date | undefined;
66
66
  packageId?: string | undefined;
67
67
  createdAt?: Date | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.27.5",
3
+ "version": "0.28.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"
@@ -1,14 +0,0 @@
1
- import type { IPackage } from "./packages";
2
- /**
3
- * Verifies that a package update signature is valid.
4
- *
5
- * Writers can create or update packages that have a dev active version.
6
- * Admins are required for packages with beta or stable active versions.
7
- *
8
- * @param packageObj The package record to verify
9
- * @param opts.isDevVersion When true, allows Writer role (for dev version operations)
10
- * @throws Error if signature is invalid or unauthorized
11
- */
12
- export declare function verifyPackageSignature(packageObj: IPackage, groupId: string, opts?: {
13
- isDevVersion?: boolean;
14
- }): Promise<void>;
@@ -1,26 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verifyPackageSignature = verifyPackageSignature;
4
- const keys_1 = require("../keys");
5
- const group_permissions_1 = require("./group-permissions");
6
- /**
7
- * Verifies that a package update signature is valid.
8
- *
9
- * Writers can create or update packages that have a dev active version.
10
- * Admins are required for packages with beta or stable active versions.
11
- *
12
- * @param packageObj The package record to verify
13
- * @param opts.isDevVersion When true, allows Writer role (for dev version operations)
14
- * @throws Error if signature is invalid or unauthorized
15
- */
16
- async function verifyPackageSignature(packageObj, groupId, opts) {
17
- (0, keys_1.verifyObjectSignature)(packageObj);
18
- const signerPublicKey = (0, keys_1.getPublicKeyFromObjectSignature)(packageObj) ?? "";
19
- const signerRole = await (0, group_permissions_1.getUserRoleFromPublicKey)(groupId, signerPublicKey);
20
- const requiredRole = opts?.isDevVersion ? group_permissions_1.GroupMemberRole.Writer : group_permissions_1.GroupMemberRole.Admin;
21
- if (signerRole < requiredRole) {
22
- throw new Error(opts?.isDevVersion
23
- ? "Only group writers or above can create or update dev packages"
24
- : "Only group admins can create or update packages");
25
- }
26
- }
@@ -1,14 +0,0 @@
1
- import { GroupMemberRole } from "./group-permissions";
2
- import type { IPackageVersion } from "./package-versions";
3
- /**
4
- * Verifies that a package version update signature is valid.
5
- *
6
- * Dev versions (`versionTag === "dev"`) require Writer role.
7
- * Beta and stable versions require Admin role.
8
- *
9
- * @param packageVersion The package version record to verify
10
- * @param groupId The group context to check roles against
11
- * @param signerRole Optional pre-resolved role (skips `getUserRoleFromPublicKey` lookup; useful in tests)
12
- * @throws Error if signature is invalid or unauthorized
13
- */
14
- export declare function verifyPackageVersionSignature(packageVersion: IPackageVersion, groupId: string, signerRole?: GroupMemberRole): Promise<void>;
@@ -1,30 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verifyPackageVersionSignature = verifyPackageVersionSignature;
4
- const keys_1 = require("../keys");
5
- const group_permissions_1 = require("./group-permissions");
6
- /**
7
- * Verifies that a package version update signature is valid.
8
- *
9
- * Dev versions (`versionTag === "dev"`) require Writer role.
10
- * Beta and stable versions require Admin role.
11
- *
12
- * @param packageVersion The package version record to verify
13
- * @param groupId The group context to check roles against
14
- * @param signerRole Optional pre-resolved role (skips `getUserRoleFromPublicKey` lookup; useful in tests)
15
- * @throws Error if signature is invalid or unauthorized
16
- */
17
- async function verifyPackageVersionSignature(packageVersion, groupId, signerRole) {
18
- (0, keys_1.verifyObjectSignature)(packageVersion);
19
- if (signerRole === undefined) {
20
- const signerPublicKey = (0, keys_1.getPublicKeyFromObjectSignature)(packageVersion) ?? "";
21
- signerRole = await (0, group_permissions_1.getUserRoleFromPublicKey)(groupId, signerPublicKey);
22
- }
23
- const isDevVersion = packageVersion.versionTag === "dev";
24
- const requiredRole = isDevVersion ? group_permissions_1.GroupMemberRole.Writer : group_permissions_1.GroupMemberRole.Admin;
25
- if (signerRole < requiredRole) {
26
- throw new Error(isDevVersion
27
- ? "Only group writers or above can create dev package versions"
28
- : "Only group admins can create or update beta/stable package versions");
29
- }
30
- }