@uns-kit/core 3.0.26 → 3.0.28

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 (51) hide show
  1. package/MIGRATIONS.md +57 -0
  2. package/dist/runtime/compatibility/energenti-authorization-v1.d.ts +95 -0
  3. package/dist/runtime/compatibility/energenti-authorization-v1.d.ts.map +1 -0
  4. package/dist/runtime/compatibility/energenti-authorization-v1.js +315 -0
  5. package/dist/runtime/compatibility/energenti-authorization-v1.js.map +1 -0
  6. package/dist/runtime/compatibility/energenti-descriptor-v1.d.ts +10 -0
  7. package/dist/runtime/compatibility/energenti-descriptor-v1.d.ts.map +1 -0
  8. package/dist/runtime/compatibility/energenti-descriptor-v1.js +18 -0
  9. package/dist/runtime/compatibility/energenti-descriptor-v1.js.map +1 -0
  10. package/dist/runtime/process-group-authorization.d.ts +111 -0
  11. package/dist/runtime/process-group-authorization.d.ts.map +1 -0
  12. package/dist/runtime/process-group-authorization.js +370 -0
  13. package/dist/runtime/process-group-authorization.js.map +1 -0
  14. package/dist/runtime/process-group-contract.d.ts +65 -0
  15. package/dist/runtime/process-group-contract.d.ts.map +1 -0
  16. package/dist/runtime/process-group-contract.js +171 -0
  17. package/dist/runtime/process-group-contract.js.map +1 -0
  18. package/dist/runtime/process-group-control-contract.d.ts +56 -0
  19. package/dist/runtime/process-group-control-contract.d.ts.map +1 -0
  20. package/dist/runtime/process-group-control-contract.js +37 -0
  21. package/dist/runtime/process-group-control-contract.js.map +1 -0
  22. package/dist/runtime/process-group-inspection.d.ts +22 -0
  23. package/dist/runtime/process-group-inspection.d.ts.map +1 -0
  24. package/dist/runtime/process-group-inspection.js +185 -0
  25. package/dist/runtime/process-group-inspection.js.map +1 -0
  26. package/dist/runtime/process-group-launch-environment.d.ts +3 -0
  27. package/dist/runtime/process-group-launch-environment.d.ts.map +1 -0
  28. package/dist/runtime/process-group-launch-environment.js +12 -0
  29. package/dist/runtime/process-group-launch-environment.js.map +1 -0
  30. package/dist/runtime/process-group-legacy-start.d.ts +2 -0
  31. package/dist/runtime/process-group-legacy-start.d.ts.map +1 -0
  32. package/dist/runtime/process-group-legacy-start.js +16 -0
  33. package/dist/runtime/process-group-legacy-start.js.map +1 -0
  34. package/dist/runtime/process-group-resources.d.ts +8 -0
  35. package/dist/runtime/process-group-resources.d.ts.map +1 -0
  36. package/dist/runtime/process-group-resources.js +96 -0
  37. package/dist/runtime/process-group-resources.js.map +1 -0
  38. package/dist/runtime/process-group-snapshot.mjs +199 -0
  39. package/dist/runtime/process-group-staging.d.ts +5 -0
  40. package/dist/runtime/process-group-staging.d.ts.map +1 -0
  41. package/dist/runtime/process-group-staging.js +63 -0
  42. package/dist/runtime/process-group-staging.js.map +1 -0
  43. package/dist/runtime/process-group-supervision.d.ts +13 -0
  44. package/dist/runtime/process-group-supervision.d.ts.map +1 -0
  45. package/dist/runtime/process-group-supervision.js +35 -0
  46. package/dist/runtime/process-group-supervision.js.map +1 -0
  47. package/dist/runtime/process-group-upgrade.d.ts +125 -0
  48. package/dist/runtime/process-group-upgrade.d.ts.map +1 -0
  49. package/dist/runtime/process-group-upgrade.js +256 -0
  50. package/dist/runtime/process-group-upgrade.js.map +1 -0
  51. package/package.json +1 -1
@@ -0,0 +1,256 @@
1
+ import fs from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import path from "node:path";
4
+ import { z } from "zod";
5
+ import { authorizeProcessGroupRuntime, processGroupAuthorizationFile, processGroupAuthorizationSchema, processGroupManifestSchema, validateProcessGroupPolicyCandidate, } from "./process-group-authorization.js";
6
+ import { normalizeProcessGroupConfiguration, processGroupDescriptor, processGroupJournalContract, processGroupJournalSchema, processGroupSourceIdentity, resolvedProcessGroupChannels, } from "./process-group-contract.js";
7
+ import { groupPlacementSchema, placementKey } from "./process-group-control-contract.js";
8
+ import { groupArtifactHash, groupChannelsHash, configurationAuthorizationHash as hash, readProcessGroupResource as read, sha256, } from "./process-group-resources.js";
9
+ const pair = z.strictObject({ publisher: groupPlacementSchema, receiver: groupPlacementSchema });
10
+ export const processGroupUpgradeInputSchema = z.strictObject({ protocolVersion: z.literal(1), source: pair, target: pair }).superRefine((v, c) => {
11
+ const all = [v.source.publisher, v.source.receiver, v.target.publisher, v.target.receiver];
12
+ if (new Set(all.map(placementKey)).size !== 4 ||
13
+ all.some((p) => p.controller !== all[0].controller || p.rttNode !== all[0].rttNode) ||
14
+ v.source.publisher.version !== v.source.receiver.version ||
15
+ v.target.publisher.version !== v.target.receiver.version ||
16
+ v.source.publisher.version === v.target.publisher.version)
17
+ c.addIssue({ code: "custom", message: "Four distinct placements, one package/controller and two paired versions required" });
18
+ });
19
+ /** Data-only same-host version review. No application JS, journal writes or launch authority. */
20
+ export function reviewInstalledProcessGroupUpgrade(configs, controllerName, stateRoot, excludedUnsFields = []) {
21
+ const first = configs.source.publisher;
22
+ const context = (c) => ({
23
+ packageRoot: c.packageRoot,
24
+ instanceDirectory: c.instanceDirectory,
25
+ rawConfiguration: c.raw,
26
+ channels: resolvedProcessGroupChannels(c.resolved),
27
+ controllerName,
28
+ stateRoot,
29
+ });
30
+ const old = authorizeProcessGroupRuntime(context(first));
31
+ const receiver = authorizeProcessGroupRuntime(context(configs.source.receiver));
32
+ if (old.role !== "target" ||
33
+ receiver.role !== "receiver" ||
34
+ old.file !== receiver.file ||
35
+ old.directory !== receiver.directory ||
36
+ !old.journalId ||
37
+ old.journalId !== receiver.journalId)
38
+ throw Error("Exact authorized source publisher/receiver pair required");
39
+ const sourcePolicy = read(old.file, true), previous = JSON.parse(sourcePolicy.toString());
40
+ const target = configs.target.publisher, descriptor = processGroupDescriptor(target.packageRoot);
41
+ if (!descriptor || descriptor.legacy)
42
+ throw Error("Target requires the shared process-group protocol declaration");
43
+ const targetConfig = normalizeProcessGroupConfiguration(target.raw, descriptor);
44
+ const journal = processGroupJournalSchema.parse(targetConfig.journal);
45
+ const resource = descriptor.manifests.find((m) => m.file === journal.groupManifest);
46
+ if (!resource)
47
+ throw Error("Unregistered target manifest");
48
+ const manifestBytes = read(path.join(target.packageRoot, resource.file)), manifest = processGroupManifestSchema.parse(JSON.parse(manifestBytes.toString()));
49
+ if (manifest.id !== resource.id || journal.directory !== old.directory || journal.journalId !== old.journalId)
50
+ throw Error("Journal or manifest identity changed");
51
+ const members = [];
52
+ const portable = (raw) => {
53
+ const copy = structuredClone(raw), uns = { ...copy["uns"] };
54
+ for (const k of excludedUnsFields)
55
+ delete uns[k];
56
+ copy["uns"] = uns;
57
+ return hash(copy);
58
+ };
59
+ for (const role of ["publisher", "receiver"]) {
60
+ const a = configs.source[role], b = configs.target[role];
61
+ if (portable(a.raw) !== portable(b.raw) ||
62
+ groupChannelsHash(resolvedProcessGroupChannels(a.resolved)) !== groupChannelsHash(resolvedProcessGroupChannels(b.resolved)))
63
+ throw Error("Configuration, broker identity, paths or units changed");
64
+ for (const side of ["source", "target"]) {
65
+ const c = configs[side][role], d = processGroupDescriptor(c.packageRoot);
66
+ if (!d)
67
+ throw Error("Missing shared runtime contract");
68
+ const normalized = normalizeProcessGroupConfiguration(c.raw, d), j = processGroupJournalSchema.parse(normalized.journal);
69
+ if (!normalized.matches ||
70
+ (role === "receiver"
71
+ ? normalized.runtimeRole !== "admission"
72
+ : normalized.runtimeRole !== undefined && normalized.runtimeRole !== "publisher") ||
73
+ j.directory !== old.directory ||
74
+ j.journalId !== old.journalId ||
75
+ sha256(read(path.join(c.packageRoot, j.groupManifest))) !== sha256(manifestBytes))
76
+ throw Error("Runtime role, manifest or journal differs");
77
+ members.push({
78
+ side,
79
+ role,
80
+ processName: String(normalized.uns["processName"]),
81
+ configurationSha256: hash(c.raw),
82
+ artifactSha256: groupArtifactHash(c.packageRoot),
83
+ });
84
+ }
85
+ }
86
+ if (members.find((m) => m.role === "publisher")?.processName !== manifest.processName ||
87
+ members.find((m) => m.role === "receiver")?.processName !== manifest.processName + "-admission")
88
+ throw Error("Manifest process identities differ");
89
+ const pkg = JSON.parse(read(path.join(target.packageRoot, "package.json")).toString());
90
+ const historical = previous.placements.filter((p) => p.role === "source");
91
+ const policy = processGroupAuthorizationSchema.parse({
92
+ schemaVersion: 3,
93
+ kind: "uns-process-group-runtime-authorization",
94
+ protocolVersion: 1,
95
+ packageName: pkg.name,
96
+ namespace: descriptor.namespace,
97
+ groupId: manifest.id,
98
+ descriptorSha256: hash(pkg.unsDatahub.processGroupRuntime),
99
+ controllerName,
100
+ packageRoot: target.packageRoot,
101
+ artifactSha256: groupArtifactHash(target.packageRoot),
102
+ groupManifestSha256: sha256(manifestBytes),
103
+ channelsSha256: groupChannelsHash(resolvedProcessGroupChannels(target.resolved)),
104
+ stateRoot,
105
+ directory: old.directory,
106
+ journalId: old.journalId,
107
+ placements: [
108
+ ...historical,
109
+ ...["publisher", "receiver"].map((role) => ({
110
+ instanceId: path.basename(configs.target[role].instanceDirectory),
111
+ processName: members.find((m) => m.side === "target" && m.role === role).processName,
112
+ role: role === "publisher" ? "target" : "receiver",
113
+ configurationSha256: hash(configs.target[role].raw),
114
+ })),
115
+ ],
116
+ });
117
+ const targetPolicy = Buffer.from(JSON.stringify(policy));
118
+ for (const role of ["publisher", "receiver"])
119
+ validateProcessGroupPolicyCandidate(context(configs.target[role]), targetPolicy);
120
+ const expected = processGroupJournalContract(processGroupSourceIdentity(resolvedProcessGroupChannels(target.resolved)[1]), journal, manifest.profiles);
121
+ const file = path.join(old.directory, "admission.sqlite");
122
+ if (fs.realpathSync(file) !== file || !fs.lstatSync(file).isFile())
123
+ throw Error("Unsafe journal resource");
124
+ const { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
125
+ const db = new DatabaseSync(file, { readOnly: true });
126
+ try {
127
+ db.exec("PRAGMA query_only=ON; PRAGMA busy_timeout=1000;");
128
+ const metadata = Object.fromEntries(db
129
+ .prepare("SELECT key,value FROM metadata WHERE key IN ('id','contract')")
130
+ .all()
131
+ .map((r) => [r["key"], r["value"]]));
132
+ if (metadata["id"] !== old.journalId || metadata["contract"] !== expected)
133
+ throw Error("Installed target cannot reopen the retained journal contract");
134
+ }
135
+ finally {
136
+ db.close();
137
+ }
138
+ if (sha256(read(old.file, true)) !== sha256(sourcePolicy))
139
+ throw Error("Source policy changed during review");
140
+ return {
141
+ review: {
142
+ protocolVersion: 1,
143
+ namespace: descriptor.namespace,
144
+ groupId: manifest.id,
145
+ policyFile: processGroupAuthorizationFile(context(target)),
146
+ sourcePolicySha256: sha256(sourcePolicy),
147
+ targetPolicySha256: sha256(targetPolicy),
148
+ journalId: old.journalId,
149
+ directory: old.directory,
150
+ journalContractSha256: sha256(expected),
151
+ profiles: manifest.profiles.map(({ processName, mappingSha256, dataGroup }) => ({ processName, mappingSha256, dataGroup })),
152
+ members,
153
+ },
154
+ sourcePolicy,
155
+ targetPolicy,
156
+ };
157
+ }
158
+ /** Atomic compare-and-swap. The authenticated host must first prove both old owners exited. */
159
+ export function replaceReviewedProcessGroupPolicy(file, expected, bytes) {
160
+ const actual = sha256(read(file, true)), next = sha256(bytes);
161
+ if (actual === next)
162
+ return; // Reconcile an already completed write after a lost reply.
163
+ if (actual !== expected)
164
+ throw Error("Runtime policy changed; review required");
165
+ const temporary = file + ".upgrade-tmp";
166
+ const fd = fs.openSync(temporary, "wx", 0o600);
167
+ try {
168
+ fs.writeFileSync(fd, bytes);
169
+ fs.fsyncSync(fd);
170
+ }
171
+ finally {
172
+ fs.closeSync(fd);
173
+ }
174
+ try {
175
+ if (sha256(read(file, true)) !== expected)
176
+ throw Error("Runtime policy changed before replacement");
177
+ fs.renameSync(temporary, file);
178
+ const directory = fs.openSync(path.dirname(file), "r");
179
+ try {
180
+ fs.fsyncSync(directory);
181
+ }
182
+ finally {
183
+ fs.closeSync(directory);
184
+ }
185
+ }
186
+ finally {
187
+ if (fs.existsSync(temporary))
188
+ fs.unlinkSync(temporary);
189
+ }
190
+ }
191
+ export const processGroupUpgradeRequestSchema = z
192
+ .strictObject({
193
+ protocolVersion: z.literal(1),
194
+ controllerName: groupPlacementSchema.shape.controller,
195
+ action: z.enum(["preview", "prepare", "get", "execute", "rollback"]),
196
+ input: processGroupUpgradeInputSchema.optional(),
197
+ id: z.string().uuid().optional(),
198
+ expectedFingerprint: z
199
+ .string()
200
+ .regex(/^[a-f0-9]{64}$/)
201
+ .optional(),
202
+ expectedRevision: z.number().int().positive().optional(),
203
+ })
204
+ .superRefine((v, c) => {
205
+ if ((["preview", "prepare"].includes(v.action) && !v.input) ||
206
+ (v.action !== "preview" && !v.id) ||
207
+ (["prepare", "execute", "rollback"].includes(v.action) && !v.expectedFingerprint) ||
208
+ (["execute", "rollback"].includes(v.action) && !v.expectedRevision) ||
209
+ (v.input && v.input.source.publisher.controller !== v.controllerName))
210
+ c.addIssue({ code: "custom", message: "Exact upgrade selector and reviewed revision required" });
211
+ });
212
+ /** Shared journal observations; only PID/start identity and fresh capture prove readiness. */
213
+ export function inspectProcessGroupUpgradeJournal(review, publisherPid, receiverPid) {
214
+ const file = path.join(review.directory, "admission.sqlite");
215
+ if (fs.realpathSync(file) !== file)
216
+ throw Error("Unsafe retained journal path");
217
+ const { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
218
+ const db = new DatabaseSync(file, { readOnly: true });
219
+ try {
220
+ db.exec("PRAGMA query_only=ON; PRAGMA busy_timeout=1000;");
221
+ const meta = Object.fromEntries(db
222
+ .prepare("SELECT key,value FROM metadata WHERE key IN ('id','contract','capture','runtime_namespace')")
223
+ .all()
224
+ .map((r) => [r["key"], r["value"]]));
225
+ if (meta["id"] !== review.journalId || sha256(String(meta["contract"])) !== review.journalContractSha256)
226
+ throw Error("Retained journal contract changed");
227
+ const capture = JSON.parse(String(meta["capture"] ?? "null"));
228
+ const identity = (pid, digest) => {
229
+ if (!pid || !Number.isSafeInteger(pid) || pid <= 0 || typeof digest !== "string" || process.platform !== "linux")
230
+ return false;
231
+ try {
232
+ const namespace = sha256(fs.readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim() + ":" + fs.readlinkSync("/proc/self/ns/pid"));
233
+ const stat = fs.readFileSync(`/proc/${pid}/stat`, "utf8"), start = stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19];
234
+ return meta["runtime_namespace"] === namespace && !!start && digest === sha256(namespace + ":" + pid + ":" + start);
235
+ }
236
+ catch {
237
+ return false;
238
+ }
239
+ };
240
+ const rows = db.prepare("SELECT name,pid,owner,runtime_identity FROM profiles").all();
241
+ return {
242
+ receiverReady: capture?.pid === receiverPid &&
243
+ ["ready", "capacity-blocked"].includes(capture?.state) &&
244
+ Number.isSafeInteger(capture?.heartbeat) &&
245
+ capture.heartbeat <= Date.now() + 1000 &&
246
+ Date.now() - capture.heartbeat <= 5000 &&
247
+ identity(receiverPid, capture.runtimeIdentity),
248
+ publisherReady: rows.length === review.profiles.length &&
249
+ review.profiles.every((p) => rows.some((r) => r["name"] === p.processName && r["pid"] === publisherPid && !!r["owner"] && identity(publisherPid, r["runtime_identity"]))),
250
+ };
251
+ }
252
+ finally {
253
+ db.close();
254
+ }
255
+ }
256
+ //# sourceMappingURL=process-group-upgrade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-group-upgrade.js","sourceRoot":"","sources":["../../src/runtime/process-group-upgrade.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport {\n authorizeProcessGroupRuntime,\n processGroupAuthorizationFile,\n processGroupAuthorizationSchema,\n processGroupManifestSchema,\n validateProcessGroupPolicyCandidate,\n} from \"./process-group-authorization.js\";\nimport {\n normalizeProcessGroupConfiguration,\n processGroupDescriptor,\n processGroupJournalContract,\n processGroupJournalSchema,\n processGroupSourceIdentity,\n resolvedProcessGroupChannels,\n} from \"./process-group-contract.js\";\nimport { groupPlacementSchema, placementKey } from \"./process-group-control-contract.js\";\nimport {\n groupArtifactHash,\n groupChannelsHash,\n configurationAuthorizationHash as hash,\n readProcessGroupResource as read,\n sha256,\n} from \"./process-group-resources.js\";\n\nconst pair = z.strictObject({ publisher: groupPlacementSchema, receiver: groupPlacementSchema });\nexport const processGroupUpgradeInputSchema = z.strictObject({ protocolVersion: z.literal(1), source: pair, target: pair }).superRefine((v, c) => {\n const all = [v.source.publisher, v.source.receiver, v.target.publisher, v.target.receiver];\n if (\n new Set(all.map(placementKey)).size !== 4 ||\n all.some((p) => p.controller !== all[0]!.controller || p.rttNode !== all[0]!.rttNode) ||\n v.source.publisher.version !== v.source.receiver.version ||\n v.target.publisher.version !== v.target.receiver.version ||\n v.source.publisher.version === v.target.publisher.version\n )\n c.addIssue({ code: \"custom\", message: \"Four distinct placements, one package/controller and two paired versions required\" });\n});\nexport type ProcessGroupUpgradeInput = z.infer<typeof processGroupUpgradeInputSchema>;\nexport type UpgradeRole = \"publisher\" | \"receiver\";\nexport type UpgradeConfiguration = {\n packageRoot: string;\n instanceDirectory: string;\n raw: Record<string, unknown>;\n resolved: Record<string, unknown>;\n};\nexport type UpgradeConfigurations = Record<\"source\" | \"target\", Record<UpgradeRole, UpgradeConfiguration>>;\nexport type GroupUpgradeReview = {\n protocolVersion: 1;\n namespace: string;\n groupId: string;\n policyFile: string;\n sourcePolicySha256: string;\n targetPolicySha256: string;\n journalId: string;\n directory: string;\n journalContractSha256: string;\n profiles: Array<{ processName: string; mappingSha256: string; dataGroup: string }>;\n members: Array<{ side: \"source\" | \"target\"; role: UpgradeRole; processName: string; configurationSha256: string; artifactSha256: string }>;\n};\nexport type PreparedGroupUpgrade = { review: GroupUpgradeReview; sourcePolicy: Buffer; targetPolicy: Buffer };\n\n/** Data-only same-host version review. No application JS, journal writes or launch authority. */\nexport function reviewInstalledProcessGroupUpgrade(\n configs: UpgradeConfigurations,\n controllerName: string,\n stateRoot: string,\n excludedUnsFields: readonly string[] = [],\n): PreparedGroupUpgrade {\n const first = configs.source.publisher;\n const context = (c: UpgradeConfiguration) => ({\n packageRoot: c.packageRoot,\n instanceDirectory: c.instanceDirectory,\n rawConfiguration: c.raw,\n channels: resolvedProcessGroupChannels(c.resolved),\n controllerName,\n stateRoot,\n });\n const old = authorizeProcessGroupRuntime(context(first));\n const receiver = authorizeProcessGroupRuntime(context(configs.source.receiver));\n if (\n old.role !== \"target\" ||\n receiver.role !== \"receiver\" ||\n old.file !== receiver.file ||\n old.directory !== receiver.directory ||\n !old.journalId ||\n old.journalId !== receiver.journalId\n )\n throw Error(\"Exact authorized source publisher/receiver pair required\");\n const sourcePolicy = read(old.file, true),\n previous = JSON.parse(sourcePolicy.toString());\n const target = configs.target.publisher,\n descriptor = processGroupDescriptor(target.packageRoot);\n if (!descriptor || descriptor.legacy) throw Error(\"Target requires the shared process-group protocol declaration\");\n const targetConfig = normalizeProcessGroupConfiguration(target.raw, descriptor);\n const journal = processGroupJournalSchema.parse(targetConfig.journal);\n const resource = descriptor.manifests.find((m) => m.file === journal.groupManifest);\n if (!resource) throw Error(\"Unregistered target manifest\");\n const manifestBytes = read(path.join(target.packageRoot, resource.file)),\n manifest = processGroupManifestSchema.parse(JSON.parse(manifestBytes.toString()));\n if (manifest.id !== resource.id || journal.directory !== old.directory || journal.journalId !== old.journalId)\n throw Error(\"Journal or manifest identity changed\");\n const members: GroupUpgradeReview[\"members\"] = [];\n const portable = (raw: Record<string, unknown>) => {\n const copy = structuredClone(raw),\n uns = { ...(copy[\"uns\"] as Record<string, unknown>) };\n for (const k of excludedUnsFields) delete uns[k];\n copy[\"uns\"] = uns;\n return hash(copy);\n };\n for (const role of [\"publisher\", \"receiver\"] as const) {\n const a = configs.source[role],\n b = configs.target[role];\n if (\n portable(a.raw) !== portable(b.raw) ||\n groupChannelsHash(resolvedProcessGroupChannels(a.resolved)) !== groupChannelsHash(resolvedProcessGroupChannels(b.resolved))\n )\n throw Error(\"Configuration, broker identity, paths or units changed\");\n for (const side of [\"source\", \"target\"] as const) {\n const c = configs[side][role],\n d = processGroupDescriptor(c.packageRoot);\n if (!d) throw Error(\"Missing shared runtime contract\");\n const normalized = normalizeProcessGroupConfiguration(c.raw, d),\n j = processGroupJournalSchema.parse(normalized.journal);\n if (\n !normalized.matches ||\n (role === \"receiver\"\n ? normalized.runtimeRole !== \"admission\"\n : normalized.runtimeRole !== undefined && normalized.runtimeRole !== \"publisher\") ||\n j.directory !== old.directory ||\n j.journalId !== old.journalId ||\n sha256(read(path.join(c.packageRoot, j.groupManifest))) !== sha256(manifestBytes)\n )\n throw Error(\"Runtime role, manifest or journal differs\");\n members.push({\n side,\n role,\n processName: String(normalized.uns[\"processName\"]),\n configurationSha256: hash(c.raw),\n artifactSha256: groupArtifactHash(c.packageRoot),\n });\n }\n }\n if (\n members.find((m) => m.role === \"publisher\")?.processName !== manifest.processName ||\n members.find((m) => m.role === \"receiver\")?.processName !== manifest.processName + \"-admission\"\n )\n throw Error(\"Manifest process identities differ\");\n const pkg = JSON.parse(read(path.join(target.packageRoot, \"package.json\")).toString());\n const historical = previous.placements.filter((p: { role: string }) => p.role === \"source\");\n const policy = processGroupAuthorizationSchema.parse({\n schemaVersion: 3,\n kind: \"uns-process-group-runtime-authorization\",\n protocolVersion: 1,\n packageName: pkg.name,\n namespace: descriptor.namespace,\n groupId: manifest.id,\n descriptorSha256: hash(pkg.unsDatahub.processGroupRuntime),\n controllerName,\n packageRoot: target.packageRoot,\n artifactSha256: groupArtifactHash(target.packageRoot),\n groupManifestSha256: sha256(manifestBytes),\n channelsSha256: groupChannelsHash(resolvedProcessGroupChannels(target.resolved)),\n stateRoot,\n directory: old.directory,\n journalId: old.journalId,\n placements: [\n ...historical,\n ...([\"publisher\", \"receiver\"] as const).map((role) => ({\n instanceId: path.basename(configs.target[role].instanceDirectory),\n processName: members.find((m) => m.side === \"target\" && m.role === role)!.processName,\n role: role === \"publisher\" ? \"target\" : \"receiver\",\n configurationSha256: hash(configs.target[role].raw),\n })),\n ],\n });\n const targetPolicy = Buffer.from(JSON.stringify(policy));\n for (const role of [\"publisher\", \"receiver\"] as const) validateProcessGroupPolicyCandidate(context(configs.target[role]), targetPolicy);\n const expected = processGroupJournalContract(\n processGroupSourceIdentity(resolvedProcessGroupChannels(target.resolved)[1]!),\n journal,\n manifest.profiles,\n );\n const file = path.join(old.directory, \"admission.sqlite\");\n if (fs.realpathSync(file) !== file || !fs.lstatSync(file).isFile()) throw Error(\"Unsafe journal resource\");\n const { DatabaseSync } = createRequire(import.meta.url)(\"node:sqlite\") as typeof import(\"node:sqlite\");\n const db = new DatabaseSync(file, { readOnly: true });\n try {\n db.exec(\"PRAGMA query_only=ON; PRAGMA busy_timeout=1000;\");\n const metadata = Object.fromEntries(\n db\n .prepare(\"SELECT key,value FROM metadata WHERE key IN ('id','contract')\")\n .all()\n .map((r) => [r[\"key\"], r[\"value\"]]),\n );\n if (metadata[\"id\"] !== old.journalId || metadata[\"contract\"] !== expected)\n throw Error(\"Installed target cannot reopen the retained journal contract\");\n } finally {\n db.close();\n }\n if (sha256(read(old.file, true)) !== sha256(sourcePolicy)) throw Error(\"Source policy changed during review\");\n return {\n review: {\n protocolVersion: 1,\n namespace: descriptor.namespace,\n groupId: manifest.id,\n policyFile: processGroupAuthorizationFile(context(target)),\n sourcePolicySha256: sha256(sourcePolicy),\n targetPolicySha256: sha256(targetPolicy),\n journalId: old.journalId,\n directory: old.directory,\n journalContractSha256: sha256(expected),\n profiles: manifest.profiles.map(({ processName, mappingSha256, dataGroup }) => ({ processName, mappingSha256, dataGroup })),\n members,\n },\n sourcePolicy,\n targetPolicy,\n };\n}\n\n/** Atomic compare-and-swap. The authenticated host must first prove both old owners exited. */\nexport function replaceReviewedProcessGroupPolicy(file: string, expected: string, bytes: Buffer): void {\n const actual = sha256(read(file, true)),\n next = sha256(bytes);\n if (actual === next) return; // Reconcile an already completed write after a lost reply.\n if (actual !== expected) throw Error(\"Runtime policy changed; review required\");\n const temporary = file + \".upgrade-tmp\";\n const fd = fs.openSync(temporary, \"wx\", 0o600);\n try {\n fs.writeFileSync(fd, bytes);\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n try {\n if (sha256(read(file, true)) !== expected) throw Error(\"Runtime policy changed before replacement\");\n fs.renameSync(temporary, file);\n const directory = fs.openSync(path.dirname(file), \"r\");\n try {\n fs.fsyncSync(directory);\n } finally {\n fs.closeSync(directory);\n }\n } finally {\n if (fs.existsSync(temporary)) fs.unlinkSync(temporary);\n }\n}\n\nexport const processGroupUpgradeRequestSchema = z\n .strictObject({\n protocolVersion: z.literal(1),\n controllerName: groupPlacementSchema.shape.controller,\n action: z.enum([\"preview\", \"prepare\", \"get\", \"execute\", \"rollback\"]),\n input: processGroupUpgradeInputSchema.optional(),\n id: z.string().uuid().optional(),\n expectedFingerprint: z\n .string()\n .regex(/^[a-f0-9]{64}$/)\n .optional(),\n expectedRevision: z.number().int().positive().optional(),\n })\n .superRefine((v, c) => {\n if (\n ([\"preview\", \"prepare\"].includes(v.action) && !v.input) ||\n (v.action !== \"preview\" && !v.id) ||\n ([\"prepare\", \"execute\", \"rollback\"].includes(v.action) && !v.expectedFingerprint) ||\n ([\"execute\", \"rollback\"].includes(v.action) && !v.expectedRevision) ||\n (v.input && v.input.source.publisher.controller !== v.controllerName)\n )\n c.addIssue({ code: \"custom\", message: \"Exact upgrade selector and reviewed revision required\" });\n });\nexport type ProcessGroupUpgradeRequest = z.infer<typeof processGroupUpgradeRequestSchema>;\n/** Shared journal observations; only PID/start identity and fresh capture prove readiness. */\nexport function inspectProcessGroupUpgradeJournal(review: GroupUpgradeReview, publisherPid?: number, receiverPid?: number) {\n const file = path.join(review.directory, \"admission.sqlite\");\n if (fs.realpathSync(file) !== file) throw Error(\"Unsafe retained journal path\");\n const { DatabaseSync } = createRequire(import.meta.url)(\"node:sqlite\") as typeof import(\"node:sqlite\");\n const db = new DatabaseSync(file, { readOnly: true });\n try {\n db.exec(\"PRAGMA query_only=ON; PRAGMA busy_timeout=1000;\");\n const meta = Object.fromEntries(\n db\n .prepare(\"SELECT key,value FROM metadata WHERE key IN ('id','contract','capture','runtime_namespace')\")\n .all()\n .map((r) => [r[\"key\"], r[\"value\"]]),\n );\n if (meta[\"id\"] !== review.journalId || sha256(String(meta[\"contract\"])) !== review.journalContractSha256)\n throw Error(\"Retained journal contract changed\");\n const capture = JSON.parse(String(meta[\"capture\"] ?? \"null\"));\n const identity = (pid: number | undefined, digest: unknown) => {\n if (!pid || !Number.isSafeInteger(pid) || pid <= 0 || typeof digest !== \"string\" || process.platform !== \"linux\") return false;\n try {\n const namespace = sha256(fs.readFileSync(\"/proc/sys/kernel/random/boot_id\", \"utf8\").trim() + \":\" + fs.readlinkSync(\"/proc/self/ns/pid\"));\n const stat = fs.readFileSync(`/proc/${pid}/stat`, \"utf8\"),\n start = stat.slice(stat.lastIndexOf(\")\") + 2).split(\" \")[19];\n return meta[\"runtime_namespace\"] === namespace && !!start && digest === sha256(namespace + \":\" + pid + \":\" + start);\n } catch {\n return false;\n }\n };\n const rows = db.prepare(\"SELECT name,pid,owner,runtime_identity FROM profiles\").all();\n return {\n receiverReady:\n capture?.pid === receiverPid &&\n [\"ready\", \"capacity-blocked\"].includes(capture?.state) &&\n Number.isSafeInteger(capture?.heartbeat) &&\n capture.heartbeat <= Date.now() + 1000 &&\n Date.now() - capture.heartbeat <= 5000 &&\n identity(receiverPid, capture.runtimeIdentity),\n publisherReady:\n rows.length === review.profiles.length &&\n review.profiles.every((p) =>\n rows.some((r) => r[\"name\"] === p.processName && r[\"pid\"] === publisherPid && !!r[\"owner\"] && identity(publisherPid, r[\"runtime_identity\"])),\n ),\n };\n } finally {\n db.close();\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uns-kit/core",
3
- "version": "3.0.26",
3
+ "version": "3.0.28",
4
4
  "description": "Core utilities and runtime building blocks for UNS-based realtime transformers.",
5
5
  "type": "module",
6
6
  "license": "MIT",