@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
|
@@ -18,6 +18,8 @@ import { type PersistentVar } from "./persistent-vars";
|
|
|
18
18
|
export interface IDevicePackagePrefs {
|
|
19
19
|
/** Which PV this device currently runs */
|
|
20
20
|
activePackageVersionId?: string;
|
|
21
|
+
/** Tag when this device explicitly selected the version; a later retag cannot opt it into dev. */
|
|
22
|
+
activePackageVersionTag?: string;
|
|
21
23
|
/** Newer selected PV temporarily bypassed because this host could not run it. */
|
|
22
24
|
compatibilityFallbackForPackageVersionId?: string;
|
|
23
25
|
/** If true, never auto-update this package */
|
|
@@ -53,6 +55,19 @@ export declare function packagePrefsVar(packageId: string, dataContext?: DataCon
|
|
|
53
55
|
* omitted the var tracks the user's default group context.
|
|
54
56
|
*/
|
|
55
57
|
export declare function updatePackagePrefs(packageId: string, prefs: Partial<IDevicePackagePrefs>, dataContext?: DataContext): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Check a selected version before evaluating any of its runtime, route or UI bundles.
|
|
60
|
+
* Group releases and shared selections must still have authorized signatures even when
|
|
61
|
+
* already on disk. Dev builds require an explicit device selection made while tagged dev.
|
|
62
|
+
*
|
|
63
|
+
* @param localDev - A host explicitly loading a local development checkout.
|
|
64
|
+
*/
|
|
65
|
+
export declare function assertPackageVersionRunnable(pkg: IPackage, pv: IPackageVersion, dataContext: DataContext, localDev?: boolean): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Verify fetched bundle bytes against the hash in the authorized version. Files rows may be
|
|
68
|
+
* changed by group members, so their own chunk hashes alone cannot authorize executable code.
|
|
69
|
+
*/
|
|
70
|
+
export declare function assertPackageBundleIntegrity(pv: IPackageVersion, kind: "package" | "routes" | "ui", contents: string | Uint8Array): void;
|
|
56
71
|
/**
|
|
57
72
|
* Effective device-local preferences for a package after merging the device's
|
|
58
73
|
* raw prefs with the group-level `IPackage` defaults. Every field is resolved
|
|
@@ -10,12 +10,16 @@
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.packagePrefsVar = packagePrefsVar;
|
|
12
12
|
exports.updatePackagePrefs = updatePackagePrefs;
|
|
13
|
+
exports.assertPackageVersionRunnable = assertPackageVersionRunnable;
|
|
14
|
+
exports.assertPackageBundleIntegrity = assertPackageBundleIntegrity;
|
|
13
15
|
exports.getEffectivePackagePrefs = getEffectivePackagePrefs;
|
|
14
16
|
exports.resolveDevicePackageVersion = resolveDevicePackageVersion;
|
|
15
17
|
exports.hasDeviceFollowOverride = hasDeviceFollowOverride;
|
|
16
18
|
exports.readStoredPackageLocalPath = readStoredPackageLocalPath;
|
|
17
19
|
const user_context_singleton_1 = require("../context/user-context-singleton");
|
|
20
|
+
const keys_1 = require("../keys");
|
|
18
21
|
const assistants_1 = require("./assistants");
|
|
22
|
+
const file_types_1 = require("./files/file.types");
|
|
19
23
|
const group_member_roles_1 = require("./group-member-roles");
|
|
20
24
|
const group_permissions_1 = require("./group-permissions");
|
|
21
25
|
const package_versions_1 = require("./package-versions");
|
|
@@ -62,6 +66,13 @@ async function updatePackagePrefs(packageId, prefs, dataContext) {
|
|
|
62
66
|
effectivePrefs = rest;
|
|
63
67
|
}
|
|
64
68
|
const next = { ...existing, ...effectivePrefs };
|
|
69
|
+
if ("activePackageVersionId" in effectivePrefs) {
|
|
70
|
+
const dc = dataContext ?? (await (0, user_context_singleton_1.getUserContext)()).defaultDataContext();
|
|
71
|
+
const selected = effectivePrefs.activePackageVersionId
|
|
72
|
+
? await (0, package_versions_1.PackageVersions)(dc).get(effectivePrefs.activePackageVersionId)
|
|
73
|
+
: undefined;
|
|
74
|
+
next.activePackageVersionTag = selected?.versionTag;
|
|
75
|
+
}
|
|
65
76
|
for (const key of Object.keys(next)) {
|
|
66
77
|
if (next[key] === undefined) {
|
|
67
78
|
delete next[key];
|
|
@@ -69,6 +80,58 @@ async function updatePackagePrefs(packageId, prefs, dataContext) {
|
|
|
69
80
|
}
|
|
70
81
|
pvar(next);
|
|
71
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Check a selected version before evaluating any of its runtime, route or UI bundles.
|
|
85
|
+
* Group releases and shared selections must still have authorized signatures even when
|
|
86
|
+
* already on disk. Dev builds require an explicit device selection made while tagged dev.
|
|
87
|
+
*
|
|
88
|
+
* @param localDev - A host explicitly loading a local development checkout.
|
|
89
|
+
*/
|
|
90
|
+
async function assertPackageVersionRunnable(pkg, pv, dataContext, localDev = false) {
|
|
91
|
+
if (!dataContext.groupId)
|
|
92
|
+
return;
|
|
93
|
+
if (pv.packageId !== pkg.packageId)
|
|
94
|
+
throw new Error("Package version belongs to another package");
|
|
95
|
+
const { isPackageRowAdminSigned } = await Promise.resolve().then(() => require("./authority-write-checks"));
|
|
96
|
+
const { getTableWriteCheck } = await Promise.resolve().then(() => require("./orm/write-checks"));
|
|
97
|
+
await getTableWriteCheck("PackageVersions")?.(undefined, pv, {
|
|
98
|
+
dataContext,
|
|
99
|
+
recordId: pv.packageVersionId,
|
|
100
|
+
});
|
|
101
|
+
if (!localDev) {
|
|
102
|
+
const prefs = packagePrefsVar(pkg.packageId, dataContext);
|
|
103
|
+
await prefs.loadingPromise;
|
|
104
|
+
let selection = prefs();
|
|
105
|
+
const deviceSelected = selection?.activePackageVersionId === pv.packageVersionId;
|
|
106
|
+
if (selection && deviceSelected && !selection.activePackageVersionTag) {
|
|
107
|
+
// A selection saved before 0.28.0 has no tag. Record this version's tag once when this
|
|
108
|
+
// device's own user signed the version (their own build or release), so it keeps running;
|
|
109
|
+
// a version someone else retagged still needs a fresh activation.
|
|
110
|
+
const me = await dataContext.userContext.getMe();
|
|
111
|
+
if (me?.publicKey && (0, keys_1.signedByKey)(pv) === me.publicKey) {
|
|
112
|
+
selection = { ...selection, activePackageVersionTag: pv.versionTag };
|
|
113
|
+
prefs(selection);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!deviceSelected && !(await isPackageRowAdminSigned(pkg, dataContext))) {
|
|
117
|
+
throw new Error("A group package selection needs an Admin's signature before it can run");
|
|
118
|
+
}
|
|
119
|
+
if (pv.versionTag === "dev" &&
|
|
120
|
+
(!deviceSelected || selection?.activePackageVersionTag !== "dev")) {
|
|
121
|
+
throw new Error("Activate this dev version on this device before running it");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Verify fetched bundle bytes against the hash in the authorized version. Files rows may be
|
|
127
|
+
* changed by group members, so their own chunk hashes alone cannot authorize executable code.
|
|
128
|
+
*/
|
|
129
|
+
function assertPackageBundleIntegrity(pv, kind, contents) {
|
|
130
|
+
const expected = pv[`${kind}BundleFileHash`];
|
|
131
|
+
if (!expected || (0, file_types_1.computeFileHash)(contents) !== expected) {
|
|
132
|
+
throw new Error(`Package ${kind} bundle hash does not match the authorized version`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
72
135
|
/**
|
|
73
136
|
* Pure function: merge raw device prefs with the group-level `IPackage`
|
|
74
137
|
* defaults to produce fully-resolved effective preferences. Used by the
|
|
@@ -221,7 +284,7 @@ async function resolveDevicePackageVersion(pkg, dataContext, opts) {
|
|
|
221
284
|
const allVersions = await (0, package_versions_1.PackageVersions)(dataContext).list({ packageId: pkg.packageId });
|
|
222
285
|
let bestPv = activePv;
|
|
223
286
|
for (const candidate of allVersions) {
|
|
224
|
-
if (candidate.packageVersionId === effectiveActivePvId)
|
|
287
|
+
if (candidate.packageVersionId === effectiveActivePvId || candidate.versionTag === "dev")
|
|
225
288
|
continue;
|
|
226
289
|
const tagOk = (0, package_versions_1.doesTagMatch)(activePv?.versionTag, candidate.versionTag, followTags, undefined);
|
|
227
290
|
if (!tagOk)
|
|
@@ -297,7 +360,10 @@ async function resolveDevicePackageVersion(pkg, dataContext, opts) {
|
|
|
297
360
|
const role = await (0, group_permissions_1.getUserRole)(groupId, userCtx.userId);
|
|
298
361
|
if (role >= group_member_roles_1.GroupMemberRole.Admin) {
|
|
299
362
|
const freshPkg = await (0, packages_1.Packages)(dataContext).get(pkg.packageId);
|
|
300
|
-
|
|
363
|
+
const { isPackageRowAdminSigned } = await Promise.resolve().then(() => require("./authority-write-checks"));
|
|
364
|
+
// Loading can span a sync that deletes and re-adds this package. Advancing a release
|
|
365
|
+
// must not endorse a Writer's replacement publisher key, URL or follow policy.
|
|
366
|
+
if (freshPkg && (await isPackageRowAdminSigned(freshPkg, dataContext))) {
|
|
301
367
|
if (freshPkg.activePackageVersionId !== bestPv.packageVersionId) {
|
|
302
368
|
freshPkg.activePackageVersionId = bestPv.packageVersionId;
|
|
303
369
|
await (0, packages_1.Packages)(dataContext).signAndSave(freshPkg);
|
|
@@ -326,7 +392,8 @@ async function loadOlderCompatiblePackageVersion(pkg, dataContext, unavailableVe
|
|
|
326
392
|
const versions = await (0, package_versions_1.PackageVersions)(dataContext).list({ packageId: pkg.packageId });
|
|
327
393
|
const candidates = versions
|
|
328
394
|
.filter((candidate) => {
|
|
329
|
-
if (candidate.packageVersionId === unavailableVersion.packageVersionId
|
|
395
|
+
if (candidate.packageVersionId === unavailableVersion.packageVersionId ||
|
|
396
|
+
candidate.versionTag === "dev") {
|
|
330
397
|
return false;
|
|
331
398
|
}
|
|
332
399
|
if ((0, package_versions_1.isNewerVersion)(unavailableVersion.version, candidate.version)) {
|
|
@@ -48,63 +48,69 @@ declare const schema: z.ZodObject<{
|
|
|
48
48
|
by: string;
|
|
49
49
|
}>, "many">>;
|
|
50
50
|
signature: z.ZodString;
|
|
51
|
+
signedContextId: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
52
|
+
signedAt: z.ZodOptional<z.ZodNumber>;
|
|
51
53
|
createdBy: z.ZodEffects<z.ZodString, string, string>;
|
|
52
54
|
createdAt: z.ZodString;
|
|
53
55
|
}, "strip", z.ZodTypeAny, {
|
|
54
56
|
version: string;
|
|
55
57
|
signature: string;
|
|
56
58
|
packageId: string;
|
|
57
|
-
createdAt: string;
|
|
58
|
-
createdBy: string;
|
|
59
59
|
packageVersionId: string;
|
|
60
60
|
packageVersionHash: string;
|
|
61
61
|
packageBundleFileId: string;
|
|
62
62
|
packageBundleFileHash: string;
|
|
63
|
+
createdBy: string;
|
|
64
|
+
createdAt: string;
|
|
63
65
|
history?: {
|
|
64
66
|
at: string;
|
|
65
67
|
action: string;
|
|
66
68
|
signature: string;
|
|
67
69
|
by: string;
|
|
68
70
|
}[] | undefined;
|
|
71
|
+
signedAt?: number | undefined;
|
|
72
|
+
signedContextId?: string | undefined;
|
|
73
|
+
versionTag?: string | undefined;
|
|
74
|
+
routesBundleFileId?: string | undefined;
|
|
75
|
+
routesBundleFileHash?: string | undefined;
|
|
76
|
+
uiBundleFileId?: string | undefined;
|
|
77
|
+
uiBundleFileHash?: string | undefined;
|
|
69
78
|
appNavs?: {
|
|
70
79
|
name: string;
|
|
71
80
|
iconClassName: string;
|
|
72
81
|
navigationPath: string;
|
|
73
82
|
displayName?: string | undefined;
|
|
74
83
|
}[] | undefined;
|
|
75
|
-
versionTag?: string | undefined;
|
|
76
|
-
routesBundleFileId?: string | undefined;
|
|
77
|
-
routesBundleFileHash?: string | undefined;
|
|
78
|
-
uiBundleFileId?: string | undefined;
|
|
79
|
-
uiBundleFileHash?: string | undefined;
|
|
80
84
|
packageAuthorSignature?: string | undefined;
|
|
81
85
|
}, {
|
|
82
86
|
version: string;
|
|
83
87
|
signature: string;
|
|
84
88
|
packageId: string;
|
|
85
|
-
createdAt: string;
|
|
86
|
-
createdBy: string;
|
|
87
89
|
packageVersionId: string;
|
|
88
90
|
packageVersionHash: string;
|
|
89
91
|
packageBundleFileId: string;
|
|
90
92
|
packageBundleFileHash: string;
|
|
93
|
+
createdBy: string;
|
|
94
|
+
createdAt: string;
|
|
91
95
|
history?: {
|
|
92
96
|
at: string;
|
|
93
97
|
action: string;
|
|
94
98
|
signature: string;
|
|
95
99
|
by: string;
|
|
96
100
|
}[] | undefined;
|
|
101
|
+
signedAt?: number | undefined;
|
|
102
|
+
signedContextId?: string | undefined;
|
|
103
|
+
versionTag?: string | undefined;
|
|
104
|
+
routesBundleFileId?: string | undefined;
|
|
105
|
+
routesBundleFileHash?: string | undefined;
|
|
106
|
+
uiBundleFileId?: string | undefined;
|
|
107
|
+
uiBundleFileHash?: string | undefined;
|
|
97
108
|
appNavs?: {
|
|
98
109
|
name: string;
|
|
99
110
|
iconClassName: string;
|
|
100
111
|
navigationPath: string;
|
|
101
112
|
displayName?: string | undefined;
|
|
102
113
|
}[] | undefined;
|
|
103
|
-
versionTag?: string | undefined;
|
|
104
|
-
routesBundleFileId?: string | undefined;
|
|
105
|
-
routesBundleFileHash?: string | undefined;
|
|
106
|
-
uiBundleFileId?: string | undefined;
|
|
107
|
-
uiBundleFileHash?: string | undefined;
|
|
108
114
|
packageAuthorSignature?: string | undefined;
|
|
109
115
|
}>;
|
|
110
116
|
/** A signed package build and its bundle hashes, tag, and lifecycle history. */
|
|
@@ -45,11 +45,12 @@ const context_1 = require("../context");
|
|
|
45
45
|
const keys_1 = require("../keys");
|
|
46
46
|
const app_nav_1 = require("../types/app-nav");
|
|
47
47
|
const zod_types_1 = require("../types/zod-types");
|
|
48
|
+
const authority_write_checks_1 = require("./authority-write-checks");
|
|
48
49
|
const decorators_1 = require("./orm/decorators");
|
|
49
50
|
const table_1 = require("./orm/table");
|
|
50
51
|
const table_definitions_system_1 = require("./orm/table-definitions.system");
|
|
51
52
|
const types_1 = require("./orm/types");
|
|
52
|
-
const
|
|
53
|
+
const write_checks_1 = require("./orm/write-checks");
|
|
53
54
|
const schema = zod_1.z.object({
|
|
54
55
|
packageVersionId: zod_types_1.zodPeerId,
|
|
55
56
|
packageId: zod_types_1.zodPeerId.describe("The package this version belongs to"),
|
|
@@ -81,6 +82,13 @@ const schema = zod_1.z.object({
|
|
|
81
82
|
.optional()
|
|
82
83
|
.describe("Audit trail of lifecycle events for this version"),
|
|
83
84
|
signature: zod_1.z.string().describe("The signed hash of this data excluding the signature itself"),
|
|
85
|
+
signedContextId: zod_types_1.zodPeerId
|
|
86
|
+
.optional()
|
|
87
|
+
.describe("The context this version approval was signed for"),
|
|
88
|
+
signedAt: zod_1.z
|
|
89
|
+
.number()
|
|
90
|
+
.optional()
|
|
91
|
+
.describe("When the signer signed this row (ms since epoch), under the signature: a copy of an earlier row can't replace a later one"),
|
|
84
92
|
createdBy: zod_types_1.zodPeerId.describe("The user who created this version"),
|
|
85
93
|
createdAt: zod_1.z.string().describe("ISO timestamp of when this version was created"),
|
|
86
94
|
});
|
|
@@ -115,14 +123,9 @@ let PackageVersionsTable = (() => {
|
|
|
115
123
|
return this.save(packageVersion, opts);
|
|
116
124
|
}
|
|
117
125
|
async save(packageVersion, opts) {
|
|
118
|
-
if (PackageVersionsTable.isPassthrough) {
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
if (!this.groupId) {
|
|
122
|
-
// users can do whatever they want to package versions in their personal space
|
|
123
|
-
return super.save(packageVersion, opts);
|
|
126
|
+
if (!PackageVersionsTable.isPassthrough) {
|
|
127
|
+
await (0, write_checks_1.checkLocalSave)(this, packageVersion, authority_write_checks_1.checkPackageVersion);
|
|
124
128
|
}
|
|
125
|
-
await (0, package_version_permissions_1.verifyPackageVersionSignature)(packageVersion, this.groupId);
|
|
126
129
|
return super.save(packageVersion, opts);
|
|
127
130
|
}
|
|
128
131
|
async signAndSave(packageVersion, opts) {
|
|
@@ -130,7 +133,10 @@ let PackageVersionsTable = (() => {
|
|
|
130
133
|
if (!sign) {
|
|
131
134
|
throw new Error("Package version signing is not enabled. Call PackageVersionsTable.enablePackageVersionSigning(fn) to enable it.");
|
|
132
135
|
}
|
|
133
|
-
packageVersion = await sign.call(PackageVersionsTable,
|
|
136
|
+
packageVersion = await sign.call(PackageVersionsTable, {
|
|
137
|
+
...packageVersion,
|
|
138
|
+
...(await (0, write_checks_1.signingStamp)(this, packageVersion)),
|
|
139
|
+
});
|
|
134
140
|
return this.save(packageVersion, opts);
|
|
135
141
|
}
|
|
136
142
|
/** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
|
package/dist/data/packages.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ declare const schema: z.ZodObject<{
|
|
|
15
15
|
updateUrl: z.ZodOptional<z.ZodString>;
|
|
16
16
|
publishPublicKey: z.ZodString;
|
|
17
17
|
signature: z.ZodString;
|
|
18
|
+
signedContextId: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
19
|
+
signedAt: z.ZodOptional<z.ZodNumber>;
|
|
18
20
|
}, "strip", z.ZodTypeAny, {
|
|
19
21
|
name: string;
|
|
20
22
|
description: string;
|
|
@@ -23,6 +25,8 @@ declare const schema: z.ZodObject<{
|
|
|
23
25
|
createdBy: string;
|
|
24
26
|
publishPublicKey: string;
|
|
25
27
|
disabled?: boolean | undefined;
|
|
28
|
+
signedAt?: number | undefined;
|
|
29
|
+
signedContextId?: string | undefined;
|
|
26
30
|
remoteRepo?: string | undefined;
|
|
27
31
|
activePackageVersionId?: string | undefined;
|
|
28
32
|
versionFollowRange?: "pinned" | "patch" | "minor" | "latest" | undefined;
|
|
@@ -36,6 +40,8 @@ declare const schema: z.ZodObject<{
|
|
|
36
40
|
createdBy: string;
|
|
37
41
|
publishPublicKey: string;
|
|
38
42
|
disabled?: boolean | undefined;
|
|
43
|
+
signedAt?: number | undefined;
|
|
44
|
+
signedContextId?: string | undefined;
|
|
39
45
|
remoteRepo?: string | undefined;
|
|
40
46
|
activePackageVersionId?: string | undefined;
|
|
41
47
|
versionFollowRange?: "pinned" | "patch" | "minor" | "latest" | undefined;
|
|
@@ -53,7 +59,6 @@ export declare class PackagesTable extends Table<IPackage> {
|
|
|
53
59
|
signAndSave(packageObj: IPackage, opts?: ISaveOptions): Promise<IPackage>;
|
|
54
60
|
/** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
|
|
55
61
|
static enablePackageSigning(fn: (packageObj: IPackage) => IPackage): void;
|
|
56
|
-
delete(packageId: string): Promise<void>;
|
|
57
62
|
}
|
|
58
63
|
/** Returns the Packages table for the given context or the current default context. */
|
|
59
64
|
export declare function Packages(dataContext?: DataContext): PackagesTable;
|
package/dist/data/packages.js
CHANGED
|
@@ -39,11 +39,12 @@ exports.Packages = Packages;
|
|
|
39
39
|
const zod_1 = require("zod");
|
|
40
40
|
const context_1 = require("../context");
|
|
41
41
|
const zod_types_1 = require("../types/zod-types");
|
|
42
|
+
const authority_write_checks_1 = require("./authority-write-checks");
|
|
42
43
|
const decorators_1 = require("./orm/decorators");
|
|
43
44
|
const table_1 = require("./orm/table");
|
|
44
45
|
const table_definitions_system_1 = require("./orm/table-definitions.system");
|
|
45
46
|
const types_1 = require("./orm/types");
|
|
46
|
-
const
|
|
47
|
+
const write_checks_1 = require("./orm/write-checks");
|
|
47
48
|
const persistent_vars_1 = require("./persistent-vars");
|
|
48
49
|
const schema = zod_1.z.object({
|
|
49
50
|
packageId: zod_types_1.zodPeerId,
|
|
@@ -75,6 +76,13 @@ const schema = zod_1.z.object({
|
|
|
75
76
|
.describe("Ed25519 public key (base64url) of the package publisher. " +
|
|
76
77
|
"Set at package creation; used as TOFU anchor for verifying packageAuthorSignature on versions."),
|
|
77
78
|
signature: zod_1.z.string().describe("The signed hash of this data excluding the signature itself"),
|
|
79
|
+
signedContextId: zod_types_1.zodPeerId
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("The context this package approval was signed for"),
|
|
82
|
+
signedAt: zod_1.z
|
|
83
|
+
.number()
|
|
84
|
+
.optional()
|
|
85
|
+
.describe("When the signer signed this row (ms since epoch), under the signature: a copy of an earlier row can't replace a later one"),
|
|
78
86
|
});
|
|
79
87
|
const metaData = {
|
|
80
88
|
name: "Packages",
|
|
@@ -106,14 +114,8 @@ let PackagesTable = (() => {
|
|
|
106
114
|
return this.save(packageObj, opts);
|
|
107
115
|
}
|
|
108
116
|
async save(packageObj, opts) {
|
|
109
|
-
if (PackagesTable.isPassthrough)
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
if (!this.groupId) {
|
|
113
|
-
// users can do whatever they want to packages in their personal space
|
|
114
|
-
return super.save(packageObj, opts);
|
|
115
|
-
}
|
|
116
|
-
await (0, package_permissions_1.verifyPackageSignature)(packageObj, this.groupId);
|
|
117
|
+
if (!PackagesTable.isPassthrough)
|
|
118
|
+
await (0, write_checks_1.checkLocalSave)(this, packageObj, authority_write_checks_1.checkPackageWrite);
|
|
117
119
|
return super.save(packageObj, opts);
|
|
118
120
|
}
|
|
119
121
|
async signAndSave(packageObj, opts) {
|
|
@@ -121,23 +123,16 @@ let PackagesTable = (() => {
|
|
|
121
123
|
if (!sign) {
|
|
122
124
|
throw new Error("Package signing is not enabled. Call PackagesTable.enablePackageSigning(fn) to enable it.");
|
|
123
125
|
}
|
|
124
|
-
packageObj = await sign.call(PackagesTable,
|
|
126
|
+
packageObj = await sign.call(PackagesTable, {
|
|
127
|
+
...packageObj,
|
|
128
|
+
...(await (0, write_checks_1.signingStamp)(this, packageObj)),
|
|
129
|
+
});
|
|
125
130
|
return this.save(packageObj, opts);
|
|
126
131
|
}
|
|
127
132
|
/** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
|
|
128
133
|
static enablePackageSigning(fn) {
|
|
129
134
|
signingGlobals[signingKey] = fn;
|
|
130
135
|
}
|
|
131
|
-
async delete(packageId) {
|
|
132
|
-
// anyone (with write permissions) can delete packages since that isn't a security risk (unless something else is relying on a package to be present?)
|
|
133
|
-
return super.delete(packageId);
|
|
134
|
-
// const userContext = await getUserContext();
|
|
135
|
-
// const userContextPackages = Packages(userContext.userDataContext);
|
|
136
|
-
// if (userContextPackages === this) {
|
|
137
|
-
// return super.delete(packageId);
|
|
138
|
-
// }
|
|
139
|
-
// throw new Error('Only deleting packages from the user context Packages table is allowed');
|
|
140
|
-
}
|
|
141
136
|
constructor() {
|
|
142
137
|
super(...arguments);
|
|
143
138
|
__runInitializers(this, _instanceExtraInitializers);
|
|
@@ -373,54 +373,17 @@ function persistentVarFactory(name, opts) {
|
|
|
373
373
|
}
|
|
374
374
|
return userContext.userDataContext;
|
|
375
375
|
}
|
|
376
|
-
/**
|
|
377
|
-
* Before storage was unified in the personal DB, a `groupDevice` /
|
|
378
|
-
* `groupUser` var created with an explicit group `dataContext` was
|
|
379
|
-
* written to that group's DB. Return that legacy location so an
|
|
380
|
-
* existing value can be migrated forward, or `undefined` when there is
|
|
381
|
-
* no distinct legacy location for this variable.
|
|
382
|
-
*/
|
|
383
|
-
function getLegacyStorageDataContext() {
|
|
384
|
-
if (scope !== "groupDevice" && scope !== "groupUser") {
|
|
385
|
-
return undefined;
|
|
386
|
-
}
|
|
387
|
-
const pinned = opts?.dataContext;
|
|
388
|
-
if (!pinned || pinned.dataContextId === userContext.userDataContext.dataContextId) {
|
|
389
|
-
return undefined;
|
|
390
|
-
}
|
|
391
|
-
return pinned;
|
|
392
|
-
}
|
|
393
376
|
function getVarNameInDb() {
|
|
394
377
|
const dc = opts?.dataContext || userContext.defaultDataContext();
|
|
395
378
|
return (0, persistent_var_identity_1.persistentVarStorageName)(name, scope, dc?.dataContextId);
|
|
396
379
|
}
|
|
397
|
-
/**
|
|
398
|
-
* Copy a record left in a group DB by the pre-unification storage rule
|
|
399
|
-
* into the personal DB. Secrets are skipped because their ciphertext is
|
|
400
|
-
* bound to the group key and cannot be moved verbatim. The legacy row is
|
|
401
|
-
* left in place: older builds on other devices may still read it, and
|
|
402
|
-
* the personal copy is authoritative from here on.
|
|
403
|
-
*/
|
|
404
|
-
async function migrateLegacyRec(table, name) {
|
|
405
|
-
const legacyContext = getLegacyStorageDataContext();
|
|
406
|
-
if (!legacyContext) {
|
|
407
|
-
return undefined;
|
|
408
|
-
}
|
|
409
|
-
const legacyRec = await PersistentVars(legacyContext).findOne({ name });
|
|
410
|
-
if (!legacyRec || legacyRec.scope !== scope || legacyRec.isSecret) {
|
|
411
|
-
return undefined;
|
|
412
|
-
}
|
|
413
|
-
const { persistentVarId: _legacyId, ...rest } = legacyRec;
|
|
414
|
-
return table.save({ ...rest, persistentVarId: "" }, { weakInsert: true });
|
|
415
|
-
}
|
|
416
380
|
async function loadRecFromDb() {
|
|
417
381
|
const dc = getStorageDataContext();
|
|
418
382
|
const table = PersistentVars(dc);
|
|
419
383
|
const name = getVarNameInDb();
|
|
420
384
|
let dbRec = await table.findOne({ name });
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
385
|
+
// Group rows are member-writable. They cannot bootstrap personal/device settings,
|
|
386
|
+
// including explicit permission to run a package's dev version on this device.
|
|
424
387
|
// Preserve an existing secret unless the caller explicitly changes that setting.
|
|
425
388
|
if (dbRec?.isSecret !== undefined && opts.isSecret === undefined) {
|
|
426
389
|
isSecret = dbRec.isSecret;
|
|
@@ -10,7 +10,7 @@ export declare const SYNC_RECOVERY_STUCK_ATTEMPTS = 5;
|
|
|
10
10
|
/** How long a still-retrying entry must have been failing before it is stuck. */
|
|
11
11
|
export declare const SYNC_RECOVERY_STUCK_AGE_MS: number;
|
|
12
12
|
/** Why automatic recovery stopped asking peers for an entry. */
|
|
13
|
-
export declare const syncRecoveryParkedReasons: readonly ["schema", "unique-conflict", "unresolvable"];
|
|
13
|
+
export declare const syncRecoveryParkedReasons: readonly ["schema", "unique-conflict", "unresolvable", "authorization"];
|
|
14
14
|
/** Why automatic recovery stopped asking peers for an entry. */
|
|
15
15
|
export type SyncRecoveryParkedReason = (typeof syncRecoveryParkedReasons)[number];
|
|
16
16
|
/** One recorded materialization failure for a synchronized record. */
|
|
@@ -50,7 +50,7 @@ export declare const syncRecoveryEntrySchema: z.ZodObject<{
|
|
|
50
50
|
}>, "many">;
|
|
51
51
|
/** Set when peer retry has stopped. Absent while the entry is still retrying. */
|
|
52
52
|
parkedAt: z.ZodOptional<z.ZodNumber>;
|
|
53
|
-
parkedReason: z.ZodOptional<z.ZodEnum<["schema", "unique-conflict", "unresolvable"]>>;
|
|
53
|
+
parkedReason: z.ZodOptional<z.ZodEnum<["schema", "unique-conflict", "unresolvable", "authorization"]>>;
|
|
54
54
|
/** Earliest time a retrying entry should ask peers again. */
|
|
55
55
|
nextAttemptAt: z.ZodOptional<z.ZodNumber>;
|
|
56
56
|
/**
|
|
@@ -71,7 +71,7 @@ export declare const syncRecoveryEntrySchema: z.ZodObject<{
|
|
|
71
71
|
sourceDeviceId?: string | undefined;
|
|
72
72
|
}[];
|
|
73
73
|
parkedAt?: number | undefined;
|
|
74
|
-
parkedReason?: "schema" | "unique-conflict" | "unresolvable" | undefined;
|
|
74
|
+
parkedReason?: "schema" | "unique-conflict" | "unresolvable" | "authorization" | undefined;
|
|
75
75
|
nextAttemptAt?: number | undefined;
|
|
76
76
|
sweepCount?: number | undefined;
|
|
77
77
|
}, {
|
|
@@ -87,7 +87,7 @@ export declare const syncRecoveryEntrySchema: z.ZodObject<{
|
|
|
87
87
|
sourceDeviceId?: string | undefined;
|
|
88
88
|
}[];
|
|
89
89
|
parkedAt?: number | undefined;
|
|
90
|
-
parkedReason?: "schema" | "unique-conflict" | "unresolvable" | undefined;
|
|
90
|
+
parkedReason?: "schema" | "unique-conflict" | "unresolvable" | "authorization" | undefined;
|
|
91
91
|
nextAttemptAt?: number | undefined;
|
|
92
92
|
sweepCount?: number | undefined;
|
|
93
93
|
}>;
|
|
@@ -20,7 +20,13 @@ exports.SYNC_RECOVERY_STUCK_ATTEMPTS = 5;
|
|
|
20
20
|
/** How long a still-retrying entry must have been failing before it is stuck. */
|
|
21
21
|
exports.SYNC_RECOVERY_STUCK_AGE_MS = 10 * 60 * 1000;
|
|
22
22
|
/** Why automatic recovery stopped asking peers for an entry. */
|
|
23
|
-
exports.syncRecoveryParkedReasons = [
|
|
23
|
+
exports.syncRecoveryParkedReasons = [
|
|
24
|
+
"schema",
|
|
25
|
+
"unique-conflict",
|
|
26
|
+
"unresolvable",
|
|
27
|
+
/** A write check refused the row; rechecked from local history when the group's authority changes. */
|
|
28
|
+
"authorization",
|
|
29
|
+
];
|
|
24
30
|
/** One recorded materialization failure for a synchronized record. */
|
|
25
31
|
exports.syncRecoveryFailureSchema = zod_1.z.object({
|
|
26
32
|
error: zod_1.z.string(),
|
|
@@ -50,8 +50,8 @@ export declare const toolAccessApprovalSchema: z.ZodObject<{
|
|
|
50
50
|
toolName: string;
|
|
51
51
|
toolId: string;
|
|
52
52
|
scope: ToolAccessApprovalScope;
|
|
53
|
-
dataContextId: string;
|
|
54
53
|
createdAt: Date;
|
|
54
|
+
dataContextId: string;
|
|
55
55
|
expiresAt: Date;
|
|
56
56
|
toolAccessApprovalId: string;
|
|
57
57
|
argsHash: string;
|
|
@@ -81,8 +81,8 @@ export declare const toolAccessApprovalSchema: z.ZodObject<{
|
|
|
81
81
|
};
|
|
82
82
|
status?: ToolAccessApprovalStatus | undefined;
|
|
83
83
|
scope?: ToolAccessApprovalScope | undefined;
|
|
84
|
-
assistantId?: string | undefined;
|
|
85
84
|
createdAt?: Date | undefined;
|
|
85
|
+
assistantId?: string | undefined;
|
|
86
86
|
workflowRunId?: string | undefined;
|
|
87
87
|
workflowLogId?: string | undefined;
|
|
88
88
|
threadMessageId?: string | undefined;
|
|
@@ -106,8 +106,8 @@ export declare function ToolAccessApprovals(dataContext?: DataContext): import("
|
|
|
106
106
|
toolName: string;
|
|
107
107
|
toolId: string;
|
|
108
108
|
scope: ToolAccessApprovalScope;
|
|
109
|
-
dataContextId: string;
|
|
110
109
|
createdAt: Date;
|
|
110
|
+
dataContextId: string;
|
|
111
111
|
expiresAt: Date;
|
|
112
112
|
toolAccessApprovalId: string;
|
|
113
113
|
argsHash: string;
|
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
import type { IUser } from "./users";
|
|
2
2
|
/**
|
|
3
|
-
* Signs a user
|
|
4
|
-
*
|
|
3
|
+
* Signs a user's profile and separately signs its initial anchor list. Later anchor changes
|
|
4
|
+
* are still authorized by the anchors on record; see {@link verifyUserSignature}.
|
|
5
5
|
* @param user The user object to sign
|
|
6
6
|
* @param secretKey The secret key to sign with
|
|
7
7
|
* @returns IUser The signed user object
|
|
8
8
|
*/
|
|
9
9
|
export declare function signUserObject(user: IUser, secretKey: string): IUser;
|
|
10
|
+
/** Whether the profile owner authorized this exact initial anchor list, before any anchors exist. */
|
|
11
|
+
export declare function hasSignedUserAnchors(user: IUser): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Whether a Users row is signed by its own `publicKey` (its anchor list is not signed): the
|
|
14
|
+
* only rows a group's authority takes a member's key from.
|
|
15
|
+
*
|
|
16
|
+
* @param user - A Users row.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isSelfSignedUser(user: IUser): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether two profiles or authenticated devices name the same user and both of their keys.
|
|
21
|
+
* This compares identity only; callers still verify signatures or the transport separately.
|
|
22
|
+
*/
|
|
23
|
+
export declare function sameUserIdentity(a: Pick<IUser, "userId" | "publicKey" | "publicBoxKey">, b: Pick<IUser, "userId" | "publicKey" | "publicBoxKey">): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* `source`'s anchor list with the owner's signature over it, to spread onto a profile or an
|
|
26
|
+
* invite identity; nothing when `source` has no list.
|
|
27
|
+
*
|
|
28
|
+
* @param source - The row or identity the list is taken from.
|
|
29
|
+
*/
|
|
30
|
+
export declare function anchorFields(source: Pick<IUser, "anchors" | "anchorsSignature">): Pick<IUser, "anchors" | "anchorsSignature">;
|
|
10
31
|
/**
|
|
11
32
|
* Verifies that a user update signature is valid, throws on invalid signature.
|
|
12
33
|
* The `anchors` list is not part of what is verified.
|
|
@@ -21,3 +42,13 @@ export declare function verifyUserSignature(user: IUser, oldUser?: IUser): void;
|
|
|
21
42
|
* @throws Error if the signature is missing, invalid, or by another key
|
|
22
43
|
*/
|
|
23
44
|
export declare function verifyUserSignedBySelf(user: IUser): void;
|
|
45
|
+
/**
|
|
46
|
+
* Whether `user`'s current key is `earlierKey`, or was handed over from it: `earlierKey` is in
|
|
47
|
+
* `keySuccession` and each link from there on is signed by its key and names the next (the
|
|
48
|
+
* last names `publicKey`). A forged profile can't produce this, since only the holder of
|
|
49
|
+
* `earlierKey` can sign its handover. The list itself sits under the profile's own signature.
|
|
50
|
+
*
|
|
51
|
+
* @param user - A profile row whose own signature has been checked.
|
|
52
|
+
* @param earlierKey - The key a grant or record names.
|
|
53
|
+
*/
|
|
54
|
+
export declare function keySucceeds(user: IUser, earlierKey: string): boolean;
|