@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.
- package/dist/context/user-context.d.ts +7 -1
- package/dist/context/user-context.js +4 -9
- package/dist/contracts/contract-proxy.d.ts +5 -0
- package/dist/contracts/contract-proxy.js +2 -2
- package/dist/contracts/contract-resolvers.d.ts +2 -2
- package/dist/contracts/contract-resolvers.js +14 -15
- package/dist/contracts/system/network-contracts.d.ts +1 -1
- package/dist/contracts/tool-catalog-service.js +1 -1
- package/dist/data/assistants.d.ts +3 -3
- package/dist/data/authority-write-checks.d.ts +42 -0
- package/dist/data/authority-write-checks.js +324 -0
- package/dist/data/change-tracking.d.ts +4 -4
- package/dist/data/create-group.js +1 -0
- package/dist/data/devices.d.ts +27 -4
- package/dist/data/devices.js +31 -0
- package/dist/data/group-authority.d.ts +111 -0
- package/dist/data/group-authority.js +281 -0
- package/dist/data/group-members.d.ts +8 -2
- package/dist/data/group-members.js +41 -18
- package/dist/data/group-permissions.d.ts +15 -31
- package/dist/data/group-permissions.js +25 -95
- package/dist/data/group-resign.d.ts +38 -0
- package/dist/data/group-resign.js +71 -0
- package/dist/data/groups.d.ts +11 -0
- package/dist/data/groups.js +31 -13
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +3 -0
- package/dist/data/invites.d.ts +14 -0
- package/dist/data/orm/index.d.ts +1 -0
- package/dist/data/orm/index.js +1 -0
- package/dist/data/orm/sql.data-source.d.ts +2 -2
- package/dist/data/orm/sql.data-source.js +24 -22
- package/dist/data/orm/table-container.js +20 -11
- package/dist/data/orm/write-checks.d.ts +89 -0
- package/dist/data/orm/write-checks.js +115 -0
- package/dist/data/package-table-identity.d.ts +21 -0
- package/dist/data/package-table-identity.js +87 -0
- package/dist/data/package-version-resolver.d.ts +15 -0
- package/dist/data/package-version-resolver.js +70 -3
- package/dist/data/package-versions.d.ts +20 -14
- package/dist/data/package-versions.js +15 -9
- package/dist/data/packages.d.ts +6 -1
- package/dist/data/packages.js +15 -20
- package/dist/data/persistent-vars.js +2 -39
- package/dist/data/sync-recovery-queue.d.ts +4 -4
- package/dist/data/sync-recovery-queue.js +7 -1
- package/dist/data/tool-access-approvals.d.ts +3 -3
- package/dist/data/user-permissions.d.ts +33 -2
- package/dist/data/user-permissions.js +87 -10
- package/dist/data/users.d.ts +49 -12
- package/dist/data/users.js +21 -1
- package/dist/data/workflows.d.ts +1 -1
- package/dist/define/index.d.ts +1 -1
- package/dist/define/index.js +2 -1
- package/dist/define/manifest.js +9 -1
- package/dist/define/table.d.ts +15 -0
- package/dist/device/binary-peer-connection-v2.d.ts +1 -1
- package/dist/device/binary-peer-connection-v2.js +1 -1
- package/dist/device/connection.d.ts +1 -1
- package/dist/device/frame-cipher.d.ts +1 -1
- package/dist/device/frame-cipher.js +1 -1
- package/dist/device/get-trust-level-fn.js +3 -6
- package/dist/device-pairing/types.d.ts +26 -26
- package/dist/identity/user-row-change.d.ts +3 -1
- package/dist/identity/user-row-change.js +33 -18
- package/dist/invites/group-approval.js +23 -24
- package/dist/invites/invite-messages.d.ts +250 -28
- package/dist/invites/invite-token.d.ts +18 -0
- package/dist/invites/invite-token.js +2 -0
- package/dist/keys.d.ts +7 -0
- package/dist/keys.js +27 -0
- package/dist/package-installer/package-installer.d.ts +1 -1
- package/dist/package-installer/package-installer.js +8 -3
- package/dist/package-installer/package-propagation.d.ts +2 -0
- package/dist/package-installer/package-propagation.js +20 -38
- package/dist/package-installer/package-remote-checker.js +15 -3
- package/dist/package-loader/isolated-artifact.d.ts +14 -0
- package/dist/package-loader/isolated-artifact.js +3 -3
- package/dist/package-loader/isolated-runtime.d.ts +3 -1
- package/dist/package-loader/isolated-table-gateway.js +4 -1
- package/dist/package-loader/package-loader.js +6 -0
- package/dist/peer-id.d.ts +19 -0
- package/dist/peer-id.js +41 -0
- package/dist/types/peer-device.d.ts +2 -0
- package/dist/types/workflow.d.ts +2 -2
- package/dist/utils.js +13 -32
- package/package.json +1 -1
- package/dist/data/package-permissions.d.ts +0 -14
- package/dist/data/package-permissions.js +0 -26
- package/dist/data/package-version-permissions.d.ts +0 -14
- 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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
157
|
-
|
|
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
|
-
*
|
|
190
|
-
*
|
|
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
|
|
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
|
-
|
|
201
|
-
|
|
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
|
-
|
|
49
|
-
if (!
|
|
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,
|
|
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
|
}
|
|
@@ -125,6 +125,20 @@ export interface IIsolatedPackageTable {
|
|
|
125
125
|
* of a contract's table shape.
|
|
126
126
|
*/
|
|
127
127
|
localOnly?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Indexes for the fields queries filter or sort by, each in order. Unique only on a local-only
|
|
130
|
+
* table. Storage policy, like `localOnly`: changing them needs a higher `versionNumber`.
|
|
131
|
+
*/
|
|
132
|
+
indexes?: IIsolatedTableIndex[];
|
|
133
|
+
}
|
|
134
|
+
/** One index of an isolated package table: field names in order, each ascending unless set. */
|
|
135
|
+
export interface IIsolatedTableIndex {
|
|
136
|
+
fields: (string | {
|
|
137
|
+
name: string;
|
|
138
|
+
order: "ASC" | "DESC";
|
|
139
|
+
})[];
|
|
140
|
+
/** No two rows share these values. Local-only tables only. */
|
|
141
|
+
unique?: true;
|
|
128
142
|
}
|
|
129
143
|
/** Name-only reference that exposes one package table through a contract. */
|
|
130
144
|
export interface IIsolatedContractTableRef {
|
|
@@ -19,6 +19,7 @@ exports.findPackageTable = findPackageTable;
|
|
|
19
19
|
exports.toConsumedContracts = toConsumedContracts;
|
|
20
20
|
const contract_definition_schema_1 = require("../contracts/contract-definition-schema");
|
|
21
21
|
const access_level_1 = require("../data/access-level");
|
|
22
|
+
const package_table_identity_1 = require("../data/package-table-identity");
|
|
22
23
|
const persistent_var_identity_1 = require("../data/persistent-var-identity");
|
|
23
24
|
const field_type_1 = require("../types/field-type");
|
|
24
25
|
const utils_1 = require("../utils");
|
|
@@ -300,7 +301,7 @@ function validatePackageTables(manifest, strict) {
|
|
|
300
301
|
if (forbiddenIdentity) {
|
|
301
302
|
throw new Error(`${tableLabel} must not declare ${forbiddenIdentity}`);
|
|
302
303
|
}
|
|
303
|
-
assertOnlyKeys(table, ["name", "description", "primaryKeyName", "fields", "versionNumber", "localOnly"], tableLabel);
|
|
304
|
+
assertOnlyKeys(table, ["name", "description", "primaryKeyName", "fields", "versionNumber", "localOnly", "indexes"], tableLabel);
|
|
304
305
|
if (typeof table.name !== "string" || table.name.trim() === "") {
|
|
305
306
|
throw new Error(`${tableLabel}.name is required`);
|
|
306
307
|
}
|
|
@@ -336,6 +337,7 @@ function validatePackageTables(manifest, strict) {
|
|
|
336
337
|
else {
|
|
337
338
|
table.versionNumber = 1;
|
|
338
339
|
}
|
|
340
|
+
(0, package_table_identity_1.validateTableIndexes)(table.indexes, new Set(table.fields.map((field) => field.name)), `${tableLabel}.indexes`, table.localOnly);
|
|
339
341
|
}
|
|
340
342
|
}
|
|
341
343
|
function validateProvidedContract(contract, index, packageTables, guestApi, strict) {
|
|
@@ -783,8 +785,6 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
|
|
|
783
785
|
"tables",
|
|
784
786
|
"network",
|
|
785
787
|
"appNavs",
|
|
786
|
-
// Voice Skill v1's retired action aliases: an older manifest may carry them; ignored.
|
|
787
|
-
"actionAliases",
|
|
788
788
|
"executionMode",
|
|
789
789
|
"guestApi",
|
|
790
790
|
], "manifest");
|
|
@@ -4,7 +4,7 @@ import type { IContractCall, IContractProviderEndpoint } from "../contracts/cont
|
|
|
4
4
|
import type { ContractRegistry } from "../contracts/registry";
|
|
5
5
|
import type { PersistentVarScope } from "../data/persistent-vars";
|
|
6
6
|
import type { IField } from "../types/field-type";
|
|
7
|
-
import type { IIsolatedContractToolRef, IIsolatedInstalledPackage, IIsolatedInstallResult, IIsolatedPackageArtifact } from "./isolated-artifact";
|
|
7
|
+
import type { IIsolatedContractToolRef, IIsolatedInstalledPackage, IIsolatedInstallResult, IIsolatedPackageArtifact, IIsolatedTableIndex } from "./isolated-artifact";
|
|
8
8
|
import type { IPackageWorkerHealth } from "./package-runtime-state";
|
|
9
9
|
/** Standard table methods exposed through isolated contract-table references. */
|
|
10
10
|
export declare const ISOLATED_CONTRACT_TABLE_OPERATIONS: readonly ["get", "list", "count", "findOne", "save", "insert", "update", "delete"];
|
|
@@ -165,6 +165,8 @@ export interface IIsolatedPackageTableDeclaration extends IIsolatedPackageTableR
|
|
|
165
165
|
versionNumber?: number;
|
|
166
166
|
/** Rows stay on the writing device; see `IIsolatedPackageTable.localOnly`. */
|
|
167
167
|
localOnly?: boolean;
|
|
168
|
+
/** Indexes; see `IIsolatedPackageTable.indexes`. */
|
|
169
|
+
indexes?: IIsolatedTableIndex[];
|
|
168
170
|
}
|
|
169
171
|
/**
|
|
170
172
|
* Trusted host capability for isolated-package table persistence.
|
|
@@ -43,6 +43,7 @@ function validateDeclaration(declaration) {
|
|
|
43
43
|
if (declaration.localOnly !== undefined && typeof declaration.localOnly !== "boolean") {
|
|
44
44
|
throw new Error("Isolated table localOnly must be a boolean");
|
|
45
45
|
}
|
|
46
|
+
(0, package_table_identity_1.validateTableIndexes)(declaration.indexes, fieldNames, "Isolated table indexes", declaration.localOnly);
|
|
46
47
|
const { localOnly, ...rest } = declaration;
|
|
47
48
|
return {
|
|
48
49
|
...rest,
|
|
@@ -61,8 +62,9 @@ function toMetaData(rawDeclaration) {
|
|
|
61
62
|
primaryKeyName: declaration.primaryKeyName,
|
|
62
63
|
fields: declaration.fields,
|
|
63
64
|
versionNumber: versionNumber(declaration),
|
|
64
|
-
// Only when set: stored definitions hash their metadata, and older ones have no such
|
|
65
|
+
// Only when set: stored definitions hash their metadata, and older ones have no such keys.
|
|
65
66
|
...(declaration.localOnly ? { localOnly: true } : {}),
|
|
67
|
+
...(declaration.indexes?.length ? { indexes: declaration.indexes } : {}),
|
|
66
68
|
};
|
|
67
69
|
}
|
|
68
70
|
function normalizedMetaData(metaData) {
|
|
@@ -111,6 +113,7 @@ function declarationFromMetaData(metaData) {
|
|
|
111
113
|
fields: metaData.fields,
|
|
112
114
|
versionNumber: metaData.versionNumber,
|
|
113
115
|
localOnly: metaData.localOnly,
|
|
116
|
+
...(metaData.indexes?.length ? { indexes: metaData.indexes } : {}),
|
|
114
117
|
});
|
|
115
118
|
}
|
|
116
119
|
function metaDataFromStoredDefinition(record, incoming) {
|
|
@@ -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) {
|
package/dist/peer-id.d.ts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
/** Builds a random string of base-36 characters with unbiased distribution (tweetnacl-backed). */
|
|
2
2
|
export declare function cryptoRandomString(length: number): string;
|
|
3
|
+
/**
|
|
4
|
+
* A 25-character peer id derived from `input`: `prefix`, then base-36 characters of its SHA-512
|
|
5
|
+
* hash. The same input always gives the same id. Each kind of derived id has its own prefix, so
|
|
6
|
+
* kinds can't collide.
|
|
7
|
+
*
|
|
8
|
+
* @param prefix - Fixed leading characters naming the kind of id.
|
|
9
|
+
* @param input - What the id is derived from.
|
|
10
|
+
*/
|
|
11
|
+
export declare function deterministicPeerId(prefix: string, input: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* A peer id derived from `key`, the same on every device. Rows different devices save under the
|
|
14
|
+
* same key are then one row, which sync merges as ordinary updates: how a synced table keeps one
|
|
15
|
+
* row per key, where a unique index can't (two devices could each save a row the other's index
|
|
16
|
+
* refuses). Start the key with what it identifies (`` `store:${name}` ``) so ids for different
|
|
17
|
+
* things can't collide.
|
|
18
|
+
*
|
|
19
|
+
* @param key - What the row stands for.
|
|
20
|
+
*/
|
|
21
|
+
export declare function idFromKey(key: string): string;
|
|
3
22
|
/**
|
|
4
23
|
* Creates a new 25-character peer id: base-36 time prefix (10 chars) + random suffix (15 chars).
|
|
5
24
|
* Sort order correlates with creation time. Use {@link isid} to validate user-provided values.
|
package/dist/peer-id.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MAX_ID = exports.MIN_ID = void 0;
|
|
4
4
|
exports.cryptoRandomString = cryptoRandomString;
|
|
5
|
+
exports.deterministicPeerId = deterministicPeerId;
|
|
6
|
+
exports.idFromKey = idFromKey;
|
|
5
7
|
exports.newid = newid;
|
|
6
8
|
exports.isid = isid;
|
|
7
9
|
exports.idTime = idTime;
|
|
@@ -35,6 +37,45 @@ function cryptoRandomString(length) {
|
|
|
35
37
|
}
|
|
36
38
|
return s.substring(0, length);
|
|
37
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Converts a byte array to a base-36 string of the requested length, using unbiased modular reduction.
|
|
42
|
+
* Bytes >= 252 are skipped to avoid bias (same technique as {@link cryptoRandomString}).
|
|
43
|
+
*/
|
|
44
|
+
function bytesToBase36(bytes, length) {
|
|
45
|
+
let s = "";
|
|
46
|
+
for (let i = 0; i < bytes.length && s.length < length; i++) {
|
|
47
|
+
const n = bytes[i];
|
|
48
|
+
if (n >= 252)
|
|
49
|
+
continue;
|
|
50
|
+
s += (n % 36).toString(36);
|
|
51
|
+
}
|
|
52
|
+
return s.substring(0, length);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A 25-character peer id derived from `input`: `prefix`, then base-36 characters of its SHA-512
|
|
56
|
+
* hash. The same input always gives the same id. Each kind of derived id has its own prefix, so
|
|
57
|
+
* kinds can't collide.
|
|
58
|
+
*
|
|
59
|
+
* @param prefix - Fixed leading characters naming the kind of id.
|
|
60
|
+
* @param input - What the id is derived from.
|
|
61
|
+
*/
|
|
62
|
+
function deterministicPeerId(prefix, input) {
|
|
63
|
+
const hashSuffixLength = 25 - prefix.length;
|
|
64
|
+
const hash = nacl.hash(new TextEncoder().encode(input));
|
|
65
|
+
return prefix + bytesToBase36(hash, hashSuffixLength);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A peer id derived from `key`, the same on every device. Rows different devices save under the
|
|
69
|
+
* same key are then one row, which sync merges as ordinary updates: how a synced table keeps one
|
|
70
|
+
* row per key, where a unique index can't (two devices could each save a row the other's index
|
|
71
|
+
* refuses). Start the key with what it identifies (`` `store:${name}` ``) so ids for different
|
|
72
|
+
* things can't collide.
|
|
73
|
+
*
|
|
74
|
+
* @param key - What the row stands for.
|
|
75
|
+
*/
|
|
76
|
+
function idFromKey(key) {
|
|
77
|
+
return deterministicPeerId("000key00", key);
|
|
78
|
+
}
|
|
38
79
|
/**
|
|
39
80
|
* Creates a new 25-character peer id: base-36 time prefix (10 chars) + random suffix (15 chars).
|
|
40
81
|
* Sort order correlates with creation time. Use {@link isid} to validate user-provided values.
|
|
@@ -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. */
|
package/dist/types/workflow.d.ts
CHANGED
|
@@ -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;
|