@uns-kit/core 3.0.33 → 3.0.35

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 (62) hide show
  1. package/MIGRATIONS.md +50 -0
  2. package/dist/config/app-config.d.ts +1 -1
  3. package/dist/config/app-config.d.ts.map +1 -1
  4. package/dist/config/app-config.js.map +1 -1
  5. package/dist/runtime/mqtt-handover-state.d.ts +48 -0
  6. package/dist/runtime/mqtt-handover-state.d.ts.map +1 -0
  7. package/dist/runtime/mqtt-handover-state.js +61 -0
  8. package/dist/runtime/mqtt-handover-state.js.map +1 -0
  9. package/dist/runtime/process-group-authorization.d.ts +72 -0
  10. package/dist/runtime/process-group-authorization.d.ts.map +1 -1
  11. package/dist/runtime/process-group-authorization.js +31 -0
  12. package/dist/runtime/process-group-authorization.js.map +1 -1
  13. package/dist/runtime/process-group-contract.d.ts +1 -0
  14. package/dist/runtime/process-group-contract.d.ts.map +1 -1
  15. package/dist/runtime/process-group-contract.js +2 -1
  16. package/dist/runtime/process-group-contract.js.map +1 -1
  17. package/dist/runtime/process-group-direct-contract.d.ts +181 -0
  18. package/dist/runtime/process-group-direct-contract.d.ts.map +1 -0
  19. package/dist/runtime/process-group-direct-contract.js +151 -0
  20. package/dist/runtime/process-group-direct-contract.js.map +1 -0
  21. package/dist/runtime/process-group-direct-review.d.ts +203 -0
  22. package/dist/runtime/process-group-direct-review.d.ts.map +1 -0
  23. package/dist/runtime/process-group-direct-review.js +263 -0
  24. package/dist/runtime/process-group-direct-review.js.map +1 -0
  25. package/dist/runtime/process-group-direct-runtime.d.ts +18 -0
  26. package/dist/runtime/process-group-direct-runtime.d.ts.map +1 -0
  27. package/dist/runtime/process-group-direct-runtime.js +154 -0
  28. package/dist/runtime/process-group-direct-runtime.js.map +1 -0
  29. package/dist/runtime/process-group-snapshot.mjs +44 -0
  30. package/dist/uns/handover-manager.d.ts +10 -1
  31. package/dist/uns/handover-manager.d.ts.map +1 -1
  32. package/dist/uns/handover-manager.js +113 -5
  33. package/dist/uns/handover-manager.js.map +1 -1
  34. package/dist/uns/uns-interfaces.d.ts +4 -0
  35. package/dist/uns/uns-interfaces.d.ts.map +1 -1
  36. package/dist/uns/uns-interfaces.js.map +1 -1
  37. package/dist/uns/uns-proxy-process.d.ts +1 -0
  38. package/dist/uns/uns-proxy-process.d.ts.map +1 -1
  39. package/dist/uns/uns-proxy-process.js +6 -2
  40. package/dist/uns/uns-proxy-process.js.map +1 -1
  41. package/dist/uns-config/config-schema.d.ts +4 -1
  42. package/dist/uns-config/config-schema.d.ts.map +1 -1
  43. package/dist/uns-config/uns-core-schema.d.ts +4 -1
  44. package/dist/uns-config/uns-core-schema.d.ts.map +1 -1
  45. package/dist/uns-config/uns-core-schema.js +1 -1
  46. package/dist/uns-config/uns-core-schema.js.map +1 -1
  47. package/dist/uns-mqtt/mqtt-proxy.d.ts +1 -0
  48. package/dist/uns-mqtt/mqtt-proxy.d.ts.map +1 -1
  49. package/dist/uns-mqtt/mqtt-proxy.js +5 -0
  50. package/dist/uns-mqtt/mqtt-proxy.js.map +1 -1
  51. package/dist/uns-mqtt/mqtt-worker.d.ts.map +1 -1
  52. package/dist/uns-mqtt/mqtt-worker.js +13 -1
  53. package/dist/uns-mqtt/mqtt-worker.js.map +1 -1
  54. package/dist/uns-mqtt/throttled-queue.d.ts +8 -1
  55. package/dist/uns-mqtt/throttled-queue.d.ts.map +1 -1
  56. package/dist/uns-mqtt/throttled-queue.js +32 -4
  57. package/dist/uns-mqtt/throttled-queue.js.map +1 -1
  58. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts +26 -2
  59. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts.map +1 -1
  60. package/dist/uns-mqtt/uns-mqtt-proxy.js +82 -6
  61. package/dist/uns-mqtt/uns-mqtt-proxy.js.map +1 -1
  62. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-group-direct-review.js","sourceRoot":"","sources":["../../src/runtime/process-group-direct-review.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { configurationGroupSnapshot, configurationResourceHash } from \"./configuration-group-contract.js\";\nimport { authorizeProcessGroupRuntime, processGroupManifestSchema } from \"./process-group-authorization.js\";\nimport {\n processGroupJournalContract,\n processGroupJournalSchema,\n processGroupSourceIdentity,\n readConfigurationSelector,\n resolvedProcessGroupChannels,\n} from \"./process-group-contract.js\";\nimport {\n type ProcessGroupDirectDescriptor,\n type ProcessGroupDirectInput,\n processGroupDirectDescriptorSchema,\n processGroupDirectInputSchema,\n processGroupRetirementPolicySchema,\n retiredJournalReferenceSchema,\n} from \"./process-group-direct-contract.js\";\nimport { describeInstalledProcessGroupGeneration, inspectGenerationJournal } from \"./process-group-generation.js\";\nimport type { GenerationJournalReference } from \"./process-group-generation.js\";\nimport {\n groupArtifactHash,\n groupChannelsHash,\n configurationAuthorizationHash as hash,\n readProcessGroupResource as read,\n sha256,\n} from \"./process-group-resources.js\";\nimport { normalizeRuntimeProcessGroup, selectedProcessGroupDescriptor } from \"./process-group-runtime-selection.js\";\nimport type { UpgradeConfiguration } from \"./process-group-upgrade.js\";\n\ntype Config = UpgradeConfiguration;\nexport type DirectTransitionConfigurations = { source: { publisher: Config; receiver: Config }; target: Config };\nconst inside = (root: string, file: string) => {\n const resolved = path.resolve(root, file);\n if (!resolved.startsWith(path.resolve(root) + path.sep)) throw Error(\"Direct resource escapes installed package\");\n return read(resolved);\n};\nexport function processGroupDirectDescriptor(packageRoot: string): ProcessGroupDirectDescriptor | undefined {\n const pkg = JSON.parse(read(path.join(packageRoot, \"package.json\")).toString());\n return pkg.unsDatahub?.processGroupDirectRuntime === undefined\n ? undefined\n : processGroupDirectDescriptorSchema.parse(pkg.unsDatahub.processGroupDirectRuntime);\n}\nexport function describeDirectProcessGroup(c: Config) {\n const descriptor = processGroupDirectDescriptor(c.packageRoot);\n if (\n !descriptor ||\n (descriptor.match && readConfigurationSelector(c.raw, descriptor.match.path) !== descriptor.match.value) ||\n readConfigurationSelector(c.raw, descriptor.runtimeRolePath) !== descriptor.directRole ||\n readConfigurationSelector(c.raw, descriptor.journalPath) !== undefined\n )\n throw Error(\"Exact declared direct mode without an input journal required\");\n const publisher = readConfigurationSelector(c.raw, descriptor.publisherPath);\n if (!publisher || typeof publisher !== \"object\" || Array.isArray(publisher)) throw Error(\"Explicit direct publisher required\");\n const selectedResources = descriptor.resources.filter(\n (r) => readConfigurationSelector(c.raw, r.kind === \"file\" ? r.manifestPath : r.groupPath) !== undefined,\n );\n if (selectedResources.length !== 1) throw Error(\"Select exactly one declared direct resource\");\n const resource = selectedResources[0]!;\n if (resource.kind === \"configuration\") {\n const snapshot = configurationGroupSnapshot(readConfigurationSelector(c.raw, resource.groupPath));\n if (\n configurationResourceHash(readConfigurationSelector(c.resolved, resource.groupPath)) !==\n configurationResourceHash(readConfigurationSelector(c.raw, resource.groupPath))\n )\n throw Error(\"Resolved direct business resource differs from its reviewed configuration\");\n return {\n descriptor,\n resource,\n groupId: snapshot.group.id,\n processName: snapshot.group.processName,\n resourceSha256: snapshot.sha256,\n profiles: snapshot.profiles.map((p) => ({ processName: p.processName, mappingSha256: p.resourceSha256, dataGroup: p.dataGroup })),\n snapshot,\n };\n }\n const manifestPath = readConfigurationSelector(c.raw, resource.manifestPath);\n const selected = resource.manifests.find((m) => m.file === manifestPath);\n if (!selected) throw Error(\"Unregistered direct manifest\");\n if (readConfigurationSelector(c.resolved, resource.manifestPath) !== selected.file) throw Error(\"Resolved direct manifest selection changed\");\n const bytes = inside(c.packageRoot, selected.file),\n manifest = processGroupManifestSchema.parse(JSON.parse(bytes.toString()));\n if (manifest.id !== selected.id) throw Error(\"Direct manifest identity differs\");\n for (const p of manifest.profiles)\n if (sha256(inside(c.packageRoot, path.join(path.dirname(selected.file), p.mappingFile))) !== p.mappingSha256)\n throw Error(\"Direct profile resource changed\");\n return {\n descriptor,\n resource,\n groupId: manifest.id,\n processName: manifest.processName,\n resourceSha256: sha256(bytes),\n profiles: manifest.profiles.map(({ processName, mappingSha256, dataGroup }) => ({ processName, mappingSha256, dataGroup })),\n manifest,\n manifestPath: selected.file,\n manifestFile: path.resolve(c.packageRoot, selected.file),\n };\n}\nfunction remove(raw: Record<string, unknown>, keys: readonly string[]): void {\n let current = raw;\n const parents: Array<[Record<string, unknown>, string]> = [];\n for (const key of keys.slice(0, -1)) {\n const next = current[key];\n if (!next || typeof next !== \"object\" || Array.isArray(next)) return;\n parents.push([current, key]);\n current = next as Record<string, unknown>;\n }\n delete current[keys.at(-1)!];\n for (const [parent, key] of parents.reverse()) {\n const value = parent[key];\n if (value && typeof value === \"object\" && !Object.keys(value).length) delete parent[key];\n else break;\n }\n}\nfunction projection(raw: Record<string, unknown>, d: ProcessGroupDirectDescriptor, excluded: readonly string[]) {\n const copy = structuredClone(raw);\n remove(copy, d.journalPath);\n remove(copy, d.runtimeRolePath);\n for (const r of d.resources) if (r.kind === \"file\") remove(copy, r.manifestPath);\n const uns = copy[\"uns\"];\n if (uns && typeof uns === \"object\" && !Array.isArray(uns)) for (const key of excluded) delete (uns as Record<string, unknown>)[key];\n // Removing a lifecycle leaf must not turn an otherwise empty publisher into a difference.\n const publisher = readConfigurationSelector(copy, d.publisherPath);\n if (publisher && typeof publisher === \"object\" && !Object.keys(publisher).length) remove(copy, d.publisherPath);\n return hash(copy);\n}\n/** Data-only installed-resource review. Does not stop, initialize, rewrite or grant launch authority. */\nexport function reviewInstalledProcessGroupDirectTransition(\n operationId: string,\n rawInput: unknown,\n configs: DirectTransitionConfigurations,\n stateRoot: string,\n excludedUnsFields: readonly string[] = [],\n) {\n const input = processGroupDirectInputSchema.parse(rawInput),\n owner = input.target.controller,\n first = configs.source.publisher,\n target = configs.target;\n const pkg = (c: Config) => JSON.parse(read(path.join(c.packageRoot, \"package.json\")).toString());\n for (const [p, c] of [\n [input.source.publisher, first],\n [input.source.receiver, configs.source.receiver],\n [input.target, target],\n ] as const) {\n if (\n path.resolve(c.packageRoot, \"instances\", p.instanceId) !== c.instanceDirectory ||\n fs.realpathSync(c.instanceDirectory) !== c.instanceDirectory ||\n pkg(c).name !== p.rttNode ||\n pkg(c).version !== p.version\n )\n throw Error(\"Exact installed package and instance placement required\");\n }\n const context = (c: Config) => ({\n packageRoot: c.packageRoot,\n instanceDirectory: c.instanceDirectory,\n rawConfiguration: c.raw,\n channels: resolvedProcessGroupChannels(c.resolved),\n controllerName: owner,\n stateRoot,\n });\n const old = authorizeProcessGroupRuntime(context(first)),\n receiver = authorizeProcessGroupRuntime(context(configs.source.receiver));\n if (\n old.role !== \"target\" ||\n receiver.role !== \"receiver\" ||\n old.file !== receiver.file ||\n old.sha256 !== receiver.sha256 ||\n old.directory !== receiver.directory ||\n !old.journalId ||\n old.journalId !== receiver.journalId\n )\n throw Error(\"Exact authorized publisher/receiver owners required\");\n const sourcePolicy = read(old.file, true),\n sourceDescriptor = selectedProcessGroupDescriptor(first.packageRoot, first.raw);\n if (!sourceDescriptor) throw Error(\"Missing source shared contract\");\n const normalized = normalizeRuntimeProcessGroup(first.raw, sourceDescriptor),\n direct = describeDirectProcessGroup(target),\n d = direct.descriptor;\n const namespace = sourceDescriptor.namespace;\n if (\n d.namespace !== namespace ||\n hash(d.publisherPath) !== hash(sourceDescriptor.publisherPath) ||\n hash(d.journalPath) !== hash(sourceDescriptor.journalPath) ||\n hash(d.runtimeRolePath) !== hash(sourceDescriptor.runtimeRolePath) ||\n direct.groupId !== old.groupId ||\n direct.processName !== old.groupProcessName ||\n (target.raw[\"uns\"] as Record<string, unknown>)?.[\"processName\"] !== old.groupProcessName ||\n projection(first.raw, d, excludedUnsFields) !== projection(target.raw, d, excludedUnsFields)\n )\n throw Error(\"Direct business configuration, selector or group identity changed\");\n const channels = groupChannelsHash(context(first).channels);\n if (channels !== groupChannelsHash(context(configs.source.receiver).channels) || channels !== groupChannelsHash(context(target).channels))\n throw Error(\"Direct broker/channel identity changed\");\n for (const c of [first, configs.source.receiver, target])\n if ((resolvedProcessGroupChannels(c.resolved)[2] as { publishQueue?: { enabled?: boolean } }).publishQueue?.enabled === true)\n throw Error(\"Initial direct retirement requires disabled disk MQTT output\");\n let reference, sourceResourceSha256: string;\n if (sourceDescriptor.protocolVersion === 2) {\n if (direct.resource.kind !== \"configuration\" || !direct.snapshot) throw Error(\"Resource kind must remain configuration based\");\n const generation = describeInstalledProcessGroupGeneration(first);\n reference = generation.reference;\n sourceResourceSha256 = configurationGroupSnapshot(readConfigurationSelector(first.raw, sourceDescriptor.groupPath)).sha256;\n } else {\n if (direct.resource.kind !== \"file\" || !direct.manifest) throw Error(\"Resource kind must remain file based\");\n const journal = processGroupJournalSchema.parse(normalized.journal);\n const declared = sourceDescriptor.manifests.find((m) => m.file === journal.groupManifest);\n if (!declared) throw Error(\"Unregistered original manifest\");\n const bytes = inside(first.packageRoot, declared.file),\n manifest = processGroupManifestSchema.parse(JSON.parse(bytes.toString()));\n sourceResourceSha256 = sha256(bytes);\n const contract = processGroupJournalContract(processGroupSourceIdentity(context(first).channels[1] as never), journal, manifest.profiles);\n reference = {\n directory: old.directory,\n journalId: old.journalId,\n journalContractSha256: sha256(contract),\n generationId: manifest.id,\n contractVersion: 1 as const,\n maxRecords: journal.maxRecords,\n maxBytes: journal.maxBytes,\n minFreeBytes: journal.minFreeBytes,\n profiles: manifest.profiles.map(({ processName, mappingSha256, dataGroup }) => ({ processName, mappingSha256, dataGroup })),\n };\n }\n const journal = retiredJournalReferenceSchema.parse(reference) as GenerationJournalReference;\n if (sourceResourceSha256 !== direct.resourceSha256 || hash(journal.profiles) !== hash(direct.profiles))\n throw Error(\"Direct profile resources, paths or units changed\");\n const usage = inspectGenerationJournal(journal); // Pending work is allowed at preview, never at policy switch.\n if (!Buffer.from(sourcePolicy.toString(), \"utf8\").equals(sourcePolicy)) throw Error(\"Original policy encoding cannot be retained exactly\");\n const member = (placement: typeof input.target, c: Config) => ({\n placement,\n processName: String((c.raw[\"uns\"] as Record<string, unknown>)[\"processName\"]),\n configurationSha256: hash(c.raw),\n artifactSha256: groupArtifactHash(c.packageRoot),\n });\n const policy = processGroupRetirementPolicySchema.parse({\n schemaVersion: 5,\n kind: \"uns-process-group-direct-retirement\",\n protocolVersion: 1,\n operationId,\n packageName: pkg(first).name,\n namespace,\n groupId: old.groupId,\n controllerName: owner,\n stateRoot,\n recovery: \"open\",\n journal,\n sourcePolicyJson: sourcePolicy.toString(),\n sourcePolicySha256: sha256(sourcePolicy),\n source: {\n publisher: {\n ...member(input.source.publisher, first),\n policyConfigurationSha256: sourceDescriptor.protocolVersion === 2 ? configurationResourceHash(first.raw) : hash(first.raw),\n },\n receiver: {\n ...member(input.source.receiver, configs.source.receiver),\n policyConfigurationSha256:\n sourceDescriptor.protocolVersion === 2 ? configurationResourceHash(configs.source.receiver.raw) : hash(configs.source.receiver.raw),\n },\n },\n target: {\n ...member(input.target, target),\n packageRoot: target.packageRoot,\n descriptorSha256: hash(d),\n resourceSha256: direct.resourceSha256,\n channelsSha256: channels,\n },\n });\n if (sha256(read(old.file, true)) !== policy.sourcePolicySha256) throw Error(\"Original policy changed during direct review\");\n const targetPolicy = Buffer.from(JSON.stringify(policy));\n if (targetPolicy.length > 65536) throw Error(\"Retirement policy byte budget exceeded\");\n return {\n review: {\n protocolVersion: 1 as const,\n kind: \"paired-to-direct\" as const,\n input,\n operationId,\n policyFile: old.file,\n sourcePolicySha256: sha256(sourcePolicy),\n targetPolicySha256: sha256(targetPolicy),\n journal,\n source: policy.source,\n target: policy.target,\n namespace,\n groupId: old.groupId,\n delivery: { inputPausePossible: true, exactlyOnceOutput: false, brokerReplayNotTransferred: true },\n usage,\n },\n sourcePolicy,\n targetPolicy,\n };\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { UpgradeConfiguration } from "./process-group-upgrade.js";
2
+ export type DirectRuntimeContext = UpgradeConfiguration & {
3
+ controllerName: string;
4
+ operationId?: string;
5
+ launchId?: string;
6
+ };
7
+ /** Startup guard, shared by host and application. An installed declaration never grants authority. */
8
+ export declare function assertProcessGroupDirectLaunch(context: DirectRuntimeContext): void;
9
+ /** PID/launch-bound observation, never authority. Start after output confirmation and input handler setup. */
10
+ export declare function trackProcessGroupDirectReadiness(context: DirectRuntimeContext, ready: () => boolean): () => void;
11
+ export declare function inspectProcessGroupDirectReadiness(directory: string, expected: {
12
+ pid: number;
13
+ launchId: string;
14
+ configurationSha256: string;
15
+ }, now?: number): boolean;
16
+ /** Preserve the managed registry in a direct process; never inherit a journal-mode launch permit. */
17
+ export declare function processGroupDirectLaunchEnvironment(operationId: string | undefined): Record<string, string>;
18
+ //# sourceMappingURL=process-group-direct-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-group-direct-runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/process-group-direct-runtime.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEvE,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,GAAG;IACxD,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AACF,sGAAsG;AACtG,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CA4ElF;AAED,8GAA8G;AAC9G,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,OAAO,GAAG,MAAM,IAAI,CAgChH;AACD,wBAAgB,kCAAkC,CAChD,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAA;CAAE,EACxE,GAAG,SAAa,GACf,OAAO,CAuBT;AAED,qGAAqG;AACrG,wBAAgB,mCAAmC,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ3G"}
@@ -0,0 +1,154 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { z } from "zod";
5
+ import { configuredGroupProcessNames, hasProcessGroupAuthorization, parseProcessGroupRetirementPolicy } from "./process-group-authorization.js";
6
+ import { resolvedProcessGroupChannels } from "./process-group-contract.js";
7
+ import { describeDirectProcessGroup, processGroupDirectDescriptor } from "./process-group-direct-review.js";
8
+ import { inspectGenerationJournal } from "./process-group-generation.js";
9
+ import { groupArtifactHash, groupChannelsHash, configurationAuthorizationHash as hash, assertPrivateProcessGroupDirectory as privateDirectory, readProcessGroupResource as read, } from "./process-group-resources.js";
10
+ /** Startup guard, shared by host and application. An installed declaration never grants authority. */
11
+ export function assertProcessGroupDirectLaunch(context) {
12
+ if (!hasProcessGroupAuthorization())
13
+ return; // Standalone deployments without a managed journal remain supported.
14
+ const names = configuredGroupProcessNames(), name = String(context.raw["uns"]?.["processName"]);
15
+ if (!names.has(name))
16
+ return;
17
+ const d = processGroupDirectDescriptor(context.packageRoot);
18
+ if (!d)
19
+ throw Error("Retained group identity requires a reviewed direct runtime");
20
+ const root = process.env["UNS_PROCESS_GROUP_STATE_ROOT"];
21
+ const directory = process.env["UNS_PROCESS_GROUP_AUTHORIZATION_DIRECTORY"];
22
+ const single = process.env["UNS_PROCESS_GROUP_AUTHORIZATION_FILE"];
23
+ const registry = path.join(root, d.namespace, "authorizations");
24
+ privateDirectory(root);
25
+ privateDirectory(registry);
26
+ if (directory && directory !== root && directory !== registry)
27
+ throw Error("Foreign direct policy registry");
28
+ const files = new Set();
29
+ if (single)
30
+ files.add(single);
31
+ if (directory) {
32
+ const entries = fs.readdirSync(registry, { withFileTypes: true });
33
+ if (entries.length > 64)
34
+ throw Error("Direct policy budget exceeded");
35
+ for (const e of entries)
36
+ if (e.name.endsWith(".json"))
37
+ files.add(path.join(registry, e.name));
38
+ }
39
+ const policies = [...files]
40
+ .map((file) => ({ file, raw: JSON.parse(read(file, true).toString()) }))
41
+ .filter((p) => p.raw.schemaVersion === 5 && p.raw.target?.processName === name);
42
+ if (policies.length !== 1)
43
+ throw Error("Group input owners remain active or retired; reviewed direct authority required");
44
+ const { file, raw } = policies[0], p = parseProcessGroupRetirementPolicy(raw), actual = describeDirectProcessGroup(context), channels = resolvedProcessGroupChannels(context.resolved), pkg = JSON.parse(read(path.join(context.packageRoot, "package.json")).toString());
45
+ if (p.stateRoot !== root ||
46
+ p.namespace !== d.namespace ||
47
+ file !== path.join(registry, p.journal.generationId + ".json") ||
48
+ context.controllerName !== p.controllerName ||
49
+ pkg.name !== p.packageName ||
50
+ actual.groupId !== p.groupId ||
51
+ actual.processName !== name ||
52
+ actual.resourceSha256 !== p.target.resourceSha256 ||
53
+ hash(actual.profiles) !== hash(p.journal.profiles))
54
+ throw Error("Retained direct group resource or identity changed");
55
+ if (path.resolve(context.packageRoot) !== context.packageRoot ||
56
+ fs.realpathSync(context.packageRoot) !== context.packageRoot ||
57
+ path.dirname(context.instanceDirectory) !== path.join(context.packageRoot, "instances") ||
58
+ fs.realpathSync(context.instanceDirectory) !== context.instanceDirectory ||
59
+ !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(path.basename(context.instanceDirectory)))
60
+ throw Error("Canonical installed direct instance required");
61
+ const usage = inspectGenerationJournal(p.journal);
62
+ if (usage.records || usage.bytes)
63
+ throw Error("Retained input journal is not empty; direct launch blocked");
64
+ if (p.recovery === "closed")
65
+ return; // Original/profile and journal-mode launches remain fenced by schema5.
66
+ if (context.packageRoot !== p.target.packageRoot ||
67
+ pkg.version !== p.target.placement.version ||
68
+ context.instanceDirectory !== path.join(context.packageRoot, "instances", p.target.placement.instanceId) ||
69
+ fs.realpathSync(context.instanceDirectory) !== context.instanceDirectory ||
70
+ hash(context.raw) !== p.target.configurationSha256 ||
71
+ groupArtifactHash(context.packageRoot) !== p.target.artifactSha256 ||
72
+ hash(d) !== p.target.descriptorSha256 ||
73
+ groupChannelsHash(channels) !== p.target.channelsSha256 ||
74
+ channels[2].publishQueue?.enabled === true)
75
+ throw Error("Exact reviewed direct placement, configuration and artifact required");
76
+ const r = JSON.parse(read(path.join(context.instanceDirectory, "process-group-reservation.json"), true).toString());
77
+ if (!z.string().uuid().safeParse(context.launchId).success ||
78
+ context.operationId !== p.operationId ||
79
+ r.operationId !== p.operationId ||
80
+ r.role !== "target" ||
81
+ r.configurationChecksum !== p.target.configurationSha256 ||
82
+ r.lastLaunch?.launchId !== context.launchId ||
83
+ !z.string().uuid().safeParse(r.lastLaunch?.requestId).success)
84
+ throw Error("Direct target requires its exact reviewed launch reservation");
85
+ }
86
+ /** PID/launch-bound observation, never authority. Start after output confirmation and input handler setup. */
87
+ export function trackProcessGroupDirectReadiness(context, ready) {
88
+ assertProcessGroupDirectLaunch(context);
89
+ if (!context.launchId)
90
+ return () => { };
91
+ const file = path.join(context.instanceDirectory, "process-group-direct-readiness.json");
92
+ let active = true;
93
+ const publish = () => {
94
+ const bytes = JSON.stringify({
95
+ protocolVersion: 1,
96
+ pid: process.pid,
97
+ launchId: context.launchId,
98
+ configurationSha256: hash(context.raw),
99
+ ready: active && ready(),
100
+ at: Date.now(),
101
+ });
102
+ const temp = file + "." + randomUUID() + ".tmp", fd = fs.openSync(temp, "wx", 0o600);
103
+ try {
104
+ fs.writeFileSync(fd, bytes);
105
+ fs.fsyncSync(fd);
106
+ }
107
+ finally {
108
+ fs.closeSync(fd);
109
+ }
110
+ fs.renameSync(temp, file);
111
+ };
112
+ publish();
113
+ const timer = setInterval(publish, 1000);
114
+ timer.unref();
115
+ return () => {
116
+ active = false;
117
+ clearInterval(timer);
118
+ publish();
119
+ };
120
+ }
121
+ export function inspectProcessGroupDirectReadiness(directory, expected, now = Date.now()) {
122
+ try {
123
+ const r = z
124
+ .strictObject({
125
+ protocolVersion: z.literal(1),
126
+ pid: z.number().int().positive(),
127
+ launchId: z.string().uuid(),
128
+ configurationSha256: z.string().regex(/^[a-f0-9]{64}$/),
129
+ ready: z.boolean(),
130
+ at: z.number().int().positive(),
131
+ })
132
+ .parse(JSON.parse(read(path.join(directory, "process-group-direct-readiness.json"), true).toString()));
133
+ return (r.ready &&
134
+ r.pid === expected.pid &&
135
+ r.launchId === expected.launchId &&
136
+ r.configurationSha256 === expected.configurationSha256 &&
137
+ r.at <= now + 1000 &&
138
+ now - r.at <= 5000);
139
+ }
140
+ catch {
141
+ return false;
142
+ }
143
+ }
144
+ /** Preserve the managed registry in a direct process; never inherit a journal-mode launch permit. */
145
+ export function processGroupDirectLaunchEnvironment(operationId) {
146
+ return {
147
+ UNS_PROCESS_GROUP_LOCAL_PILOT: "0",
148
+ UNS_PROCESS_GROUP_AUTHORIZATION_FILE: process.env["UNS_PROCESS_GROUP_AUTHORIZATION_FILE"] ?? "",
149
+ UNS_PROCESS_GROUP_AUTHORIZATION_DIRECTORY: process.env["UNS_PROCESS_GROUP_AUTHORIZATION_DIRECTORY"] ?? "",
150
+ UNS_PROCESS_GROUP_STATE_ROOT: process.env["UNS_PROCESS_GROUP_STATE_ROOT"] ?? "",
151
+ UNS_PROCESS_GROUP_OPERATION_ID: operationId ?? "",
152
+ };
153
+ }
154
+ //# sourceMappingURL=process-group-direct-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-group-direct-runtime.js","sourceRoot":"","sources":["../../src/runtime/process-group-direct-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { configuredGroupProcessNames, hasProcessGroupAuthorization, parseProcessGroupRetirementPolicy } from \"./process-group-authorization.js\";\nimport { resolvedProcessGroupChannels } from \"./process-group-contract.js\";\nimport { describeDirectProcessGroup, processGroupDirectDescriptor } from \"./process-group-direct-review.js\";\nimport { type GenerationJournalReference, inspectGenerationJournal } from \"./process-group-generation.js\";\nimport {\n groupArtifactHash,\n groupChannelsHash,\n configurationAuthorizationHash as hash,\n assertPrivateProcessGroupDirectory as privateDirectory,\n readProcessGroupResource as read,\n} from \"./process-group-resources.js\";\nimport type { UpgradeConfiguration } from \"./process-group-upgrade.js\";\n\nexport type DirectRuntimeContext = UpgradeConfiguration & {\n controllerName: string;\n operationId?: string;\n launchId?: string;\n};\n/** Startup guard, shared by host and application. An installed declaration never grants authority. */\nexport function assertProcessGroupDirectLaunch(context: DirectRuntimeContext): void {\n if (!hasProcessGroupAuthorization()) return; // Standalone deployments without a managed journal remain supported.\n const names = configuredGroupProcessNames(),\n name = String((context.raw[\"uns\"] as Record<string, unknown>)?.[\"processName\"]);\n if (!names.has(name)) return;\n const d = processGroupDirectDescriptor(context.packageRoot);\n if (!d) throw Error(\"Retained group identity requires a reviewed direct runtime\");\n const root = process.env[\"UNS_PROCESS_GROUP_STATE_ROOT\"]!;\n const directory = process.env[\"UNS_PROCESS_GROUP_AUTHORIZATION_DIRECTORY\"];\n const single = process.env[\"UNS_PROCESS_GROUP_AUTHORIZATION_FILE\"];\n const registry = path.join(root, d.namespace, \"authorizations\");\n privateDirectory(root);\n privateDirectory(registry);\n if (directory && directory !== root && directory !== registry) throw Error(\"Foreign direct policy registry\");\n const files = new Set<string>();\n if (single) files.add(single);\n if (directory) {\n const entries = fs.readdirSync(registry, { withFileTypes: true });\n if (entries.length > 64) throw Error(\"Direct policy budget exceeded\");\n for (const e of entries) if (e.name.endsWith(\".json\")) files.add(path.join(registry, e.name));\n }\n const policies = [...files]\n .map((file) => ({ file, raw: JSON.parse(read(file, true).toString()) }))\n .filter((p) => p.raw.schemaVersion === 5 && p.raw.target?.processName === name);\n if (policies.length !== 1) throw Error(\"Group input owners remain active or retired; reviewed direct authority required\");\n const { file, raw } = policies[0]!,\n p = parseProcessGroupRetirementPolicy(raw),\n actual = describeDirectProcessGroup(context),\n channels = resolvedProcessGroupChannels(context.resolved),\n pkg = JSON.parse(read(path.join(context.packageRoot, \"package.json\")).toString());\n if (\n p.stateRoot !== root ||\n p.namespace !== d.namespace ||\n file !== path.join(registry, p.journal.generationId + \".json\") ||\n context.controllerName !== p.controllerName ||\n pkg.name !== p.packageName ||\n actual.groupId !== p.groupId ||\n actual.processName !== name ||\n actual.resourceSha256 !== p.target.resourceSha256 ||\n hash(actual.profiles) !== hash(p.journal.profiles)\n )\n throw Error(\"Retained direct group resource or identity changed\");\n if (\n path.resolve(context.packageRoot) !== context.packageRoot ||\n fs.realpathSync(context.packageRoot) !== context.packageRoot ||\n path.dirname(context.instanceDirectory) !== path.join(context.packageRoot, \"instances\") ||\n fs.realpathSync(context.instanceDirectory) !== context.instanceDirectory ||\n !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(path.basename(context.instanceDirectory))\n )\n throw Error(\"Canonical installed direct instance required\");\n const usage = inspectGenerationJournal(p.journal as GenerationJournalReference);\n if (usage.records || usage.bytes) throw Error(\"Retained input journal is not empty; direct launch blocked\");\n if (p.recovery === \"closed\") return; // Original/profile and journal-mode launches remain fenced by schema5.\n if (\n context.packageRoot !== p.target.packageRoot ||\n pkg.version !== p.target.placement.version ||\n context.instanceDirectory !== path.join(context.packageRoot, \"instances\", p.target.placement.instanceId) ||\n fs.realpathSync(context.instanceDirectory) !== context.instanceDirectory ||\n hash(context.raw) !== p.target.configurationSha256 ||\n groupArtifactHash(context.packageRoot) !== p.target.artifactSha256 ||\n hash(d) !== p.target.descriptorSha256 ||\n groupChannelsHash(channels) !== p.target.channelsSha256 ||\n (channels[2] as { publishQueue?: { enabled?: boolean } }).publishQueue?.enabled === true\n )\n throw Error(\"Exact reviewed direct placement, configuration and artifact required\");\n const r = JSON.parse(read(path.join(context.instanceDirectory, \"process-group-reservation.json\"), true).toString());\n if (\n !z.string().uuid().safeParse(context.launchId).success ||\n context.operationId !== p.operationId ||\n r.operationId !== p.operationId ||\n r.role !== \"target\" ||\n r.configurationChecksum !== p.target.configurationSha256 ||\n r.lastLaunch?.launchId !== context.launchId ||\n !z.string().uuid().safeParse(r.lastLaunch?.requestId).success\n )\n throw Error(\"Direct target requires its exact reviewed launch reservation\");\n}\n\n/** PID/launch-bound observation, never authority. Start after output confirmation and input handler setup. */\nexport function trackProcessGroupDirectReadiness(context: DirectRuntimeContext, ready: () => boolean): () => void {\n assertProcessGroupDirectLaunch(context);\n if (!context.launchId) return () => {};\n const file = path.join(context.instanceDirectory, \"process-group-direct-readiness.json\");\n let active = true;\n const publish = () => {\n const bytes = JSON.stringify({\n protocolVersion: 1,\n pid: process.pid,\n launchId: context.launchId,\n configurationSha256: hash(context.raw),\n ready: active && ready(),\n at: Date.now(),\n });\n const temp = file + \".\" + randomUUID() + \".tmp\",\n fd = fs.openSync(temp, \"wx\", 0o600);\n try {\n fs.writeFileSync(fd, bytes);\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n fs.renameSync(temp, file);\n };\n publish();\n const timer = setInterval(publish, 1000);\n timer.unref();\n return () => {\n active = false;\n clearInterval(timer);\n publish();\n };\n}\nexport function inspectProcessGroupDirectReadiness(\n directory: string,\n expected: { pid: number; launchId: string; configurationSha256: string },\n now = Date.now(),\n): boolean {\n try {\n const r = z\n .strictObject({\n protocolVersion: z.literal(1),\n pid: z.number().int().positive(),\n launchId: z.string().uuid(),\n configurationSha256: z.string().regex(/^[a-f0-9]{64}$/),\n ready: z.boolean(),\n at: z.number().int().positive(),\n })\n .parse(JSON.parse(read(path.join(directory, \"process-group-direct-readiness.json\"), true).toString()));\n return (\n r.ready &&\n r.pid === expected.pid &&\n r.launchId === expected.launchId &&\n r.configurationSha256 === expected.configurationSha256 &&\n r.at <= now + 1000 &&\n now - r.at <= 5000\n );\n } catch {\n return false;\n }\n}\n\n/** Preserve the managed registry in a direct process; never inherit a journal-mode launch permit. */\nexport function processGroupDirectLaunchEnvironment(operationId: string | undefined): Record<string, string> {\n return {\n UNS_PROCESS_GROUP_LOCAL_PILOT: \"0\",\n UNS_PROCESS_GROUP_AUTHORIZATION_FILE: process.env[\"UNS_PROCESS_GROUP_AUTHORIZATION_FILE\"] ?? \"\",\n UNS_PROCESS_GROUP_AUTHORIZATION_DIRECTORY: process.env[\"UNS_PROCESS_GROUP_AUTHORIZATION_DIRECTORY\"] ?? \"\",\n UNS_PROCESS_GROUP_STATE_ROOT: process.env[\"UNS_PROCESS_GROUP_STATE_ROOT\"] ?? \"\",\n UNS_PROCESS_GROUP_OPERATION_ID: operationId ?? \"\",\n };\n}\n"]}
@@ -79,6 +79,7 @@ const namespaces = rootEntries
79
79
  .sort();
80
80
  if (namespaces.length > 64) fail();
81
81
  const capturedGroups = new Set();
82
+ const retirementChecks = new Map();
82
83
  for (const namespace of namespaces) {
83
84
  if (!/^[a-z][a-z0-9-]{0,99}$/.test(namespace)) fail();
84
85
  const namespaceRoot = path.join(root, namespace),
@@ -240,9 +241,52 @@ for (const namespace of namespaces) {
240
241
  if (snapshotBytes > limits.maximumSnapshotBytes) fail();
241
242
  info[name === "admission.sqlite" ? "admissionSha256" : "ownershipSha256"] = fileHash(p);
242
243
  }
244
+ // Retired ownership is part of recovery evidence, never an automatic launch permit.
245
+ // Version2 makes an older transport fail closed instead of silently omitting it.
246
+ const policyDirectory=path.join(namespaceRoot,"authorizations"),policyFile=path.join(policyDirectory,group+".json");
247
+ let policyExists=false;
248
+ try{fs.lstatSync(policyFile);policyExists=true;}catch(e){if(e.code!=="ENOENT")throw e;}
249
+ if(policyExists){
250
+ check(policyDirectory,true);check(policyFile);
251
+ if(fs.statSync(policyFile).size>65536)fail();
252
+ const policy=fs.readFileSync(policyFile),p=JSON.parse(policy.toString());
253
+ if(p.schemaVersion===5){
254
+ if(info.usage.records!==0||info.usage.bytes!==0||p.kind!=="uns-process-group-direct-retirement"||p.protocolVersion!==1||p.stateRoot!==root||p.namespace!==namespace||
255
+ p.journal?.directory!==source||p.journal?.generationId!==group||p.journal?.journalId!==info.journalId||
256
+ p.journal?.journalContractSha256!==info.contractSha256||typeof p.sourcePolicyJson!=="string"||Buffer.byteLength(p.sourcePolicyJson)>32768||hash(p.sourcePolicyJson)!==p.sourcePolicySha256||
257
+ !["open","closed"].includes(p.recovery))fail();
258
+ fs.writeFileSync(path.join(target,"retirement-policy.json"),policy,{mode:0o600,flag:"wx"});
259
+ info.retirementPolicySha256=hash(policy);info.retirementPolicyBytes=policy.length;
260
+ snapshotBytes+=policy.length;if(snapshotBytes>limits.maximumSnapshotBytes)fail();
261
+ // Freeze this observation; a concurrent policy switch requires a new consistent capture.
262
+ if(!fs.readFileSync(policyFile).equals(policy))fail();
263
+ manifest.schemaVersion=2;
264
+ retirementChecks.set(policyFile,hash(policy));
265
+ }
266
+ }
243
267
  manifest.groups.push(info);
244
268
  }
245
269
  }
270
+ // Direct configurations no longer name the old journal. Its retained policy must
271
+ // still resolve to captured state, even after the stopped source configs are removed.
272
+ let registryFiles=0;
273
+ for(const namespace of namespaces){
274
+ const registry=path.join(root,namespace,"authorizations");
275
+ let entries;
276
+ try{check(registry,true);entries=fs.readdirSync(registry,{withFileTypes:true});}catch(e){if(e.code==="ENOENT")continue;throw e;}
277
+ if(entries.length>64||entries.some(e=>e.isSymbolicLink()))fail();
278
+ for(const entry of entries){
279
+ if(!entry.name.endsWith(".json"))continue;
280
+ if(++registryFiles>64||!entry.isFile()||!/^([a-z][a-z0-9-]{0,99})\.json$/.test(entry.name))fail();
281
+ const file=path.join(registry,entry.name);check(file);if(fs.statSync(file).size>65536)fail();
282
+ const bytes=fs.readFileSync(file),policy=JSON.parse(bytes.toString());
283
+ if(policy.schemaVersion!==5)continue;
284
+ const captured=manifest.groups.find(g=>g.namespace===namespace&&g.group===policy.journal?.generationId);
285
+ if(!captured||captured.retirementPolicySha256!==hash(bytes)||captured.journalId!==policy.journal?.journalId||
286
+ policy.namespace!==namespace||policy.stateRoot!==root||entry.name!==policy.journal.generationId+".json")fail();
287
+ }
288
+ }
289
+ for(const [file,digest] of retirementChecks){check(file);if(hash(fs.readFileSync(file))!==digest)fail();}
246
290
  const remaining = fs.statfsSync(output, { bigint: true });
247
291
  if (remaining.bavail * remaining.bsize < BigInt(snapshotBytes + 64 * 1024 * 1024)) fail();
248
292
  const manifestBytes = JSON.stringify(manifest) + "\n";
@@ -10,6 +10,7 @@ import { UnsEvents } from "./uns-interfaces.js";
10
10
  * and processing handover responses.
11
11
  */
12
12
  export declare class HandoverManager {
13
+ private readonly statefulHandover;
13
14
  event: HandoverManagerEventEmitter<HandoverManagerEvents>;
14
15
  private processName;
15
16
  private processId;
@@ -26,10 +27,16 @@ export declare class HandoverManager {
26
27
  private sourcePreparation;
27
28
  private sourceExit;
28
29
  private readonly shutdownHooks;
30
+ private expectedSourceId;
31
+ private preparedState;
32
+ private stateDigest;
33
+ private acceptedStateDigest;
34
+ private targetPreparation;
35
+ private finRetry;
29
36
  handoverRequestEnabled: boolean;
30
37
  handoverEnabled: boolean;
31
38
  forceStartEnabled: boolean;
32
- constructor(processName: string, processId: string, mqttProxy: MqttProxy, unsMqttProxies: UnsMqttProxy[], handoverRequestEnabled: boolean, handoverEnabled: boolean, forceStartEnabled: boolean, handoverId?: string, shutdownHooks?: HandoverShutdownHooks);
39
+ constructor(processName: string, processId: string, mqttProxy: MqttProxy, unsMqttProxies: UnsMqttProxy[], handoverRequestEnabled: boolean, handoverEnabled: boolean, forceStartEnabled: boolean, handoverId?: string, shutdownHooks?: HandoverShutdownHooks, statefulHandover?: boolean);
33
40
  private getUserProperties;
34
41
  private handoverPayload;
35
42
  private getHandoverId;
@@ -47,6 +54,8 @@ export declare class HandoverManager {
47
54
  */
48
55
  private exitSource;
49
56
  private prepareSourceHandover;
57
+ private sendPreparedState;
58
+ private acceptPreparedState;
50
59
  private finishSourceHandover;
51
60
  private handleHandover;
52
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"handover-manager.d.ts","sourceRoot":"","sources":["../../src/uns/handover-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAiD,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACnH,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,SAAS,MAAM,2BAA2B,CAAC;AAElD,OAAO,YAAY,MAAM,+BAA+B,CAAC;AACzD,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAElF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAQhD;;;;GAIG;AACH,qBAAa,eAAe;IACnB,KAAK,EAAE,2BAA2B,CAAC,qBAAqB,CAAC,CAA4D;IAC5H,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoC;IAC3D,sBAAsB,EAAE,OAAO,CAAS;IACxC,eAAe,EAAE,OAAO,CAAQ;IAChC,iBAAiB,EAAE,OAAO,CAAS;gBAGxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,YAAY,EAAE,EAC9B,sBAAsB,EAAE,OAAO,EAC/B,eAAe,EAAE,OAAO,EACxB,iBAAiB,EAAE,OAAO,EAC1B,UAAU,CAAC,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,qBAAqB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,oBAAoB;IAU5B;;;OAGG;IACU,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoFxE;;OAEG;IACH,OAAO,CAAC,UAAU;YAIJ,qBAAqB;YA6DrB,oBAAoB;YAiBpB,cAAc;CA2F7B"}
1
+ {"version":3,"file":"handover-manager.d.ts","sourceRoot":"","sources":["../../src/uns/handover-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiD,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACnH,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,SAAS,MAAM,2BAA2B,CAAC;AAElD,OAAO,YAAY,MAAM,+BAA+B,CAAC;AACzD,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAElF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAQhD;;;;GAIG;AACH,qBAAa,eAAe;IAqCxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IApC5B,KAAK,EAAE,2BAA2B,CAAC,qBAAqB,CAAC,CAA4D;IAC5H,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoC;IAClE,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,QAAQ,CAA6B;IACtC,sBAAsB,EAAE,OAAO,CAAS;IACxC,eAAe,EAAE,OAAO,CAAQ;IAChC,iBAAiB,EAAE,OAAO,CAAS;gBAGxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,YAAY,EAAE,EAC9B,sBAAsB,EAAE,OAAO,EAC/B,eAAe,EAAE,OAAO,EACxB,iBAAiB,EAAE,OAAO,EAC1B,UAAU,CAAC,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,qBAAqB,EACpB,gBAAgB,UAAQ;IAkC3C,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,oBAAoB;IAU5B;;;OAGG;IACU,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqFxE;;OAEG;IACH,OAAO,CAAC,UAAU;YAIJ,qBAAqB;YA8ErB,iBAAiB;YAOjB,mBAAmB;YAkBnB,oBAAoB;YA2BpB,cAAc;CAwH7B"}
@@ -1,3 +1,4 @@
1
+ import { mqttHandoverOfferSchema, parseMqttHandoverState, mqttHandoverStateDigest } from "../runtime/mqtt-handover-state.js";
1
2
  import logger from "../logger.js";
2
3
  import { runHandoverShutdown, validateHandoverShutdown } from "./handover-shutdown.js";
3
4
  import { MqttTopicBuilder } from "../uns-mqtt/mqtt-topic-builder.js";
@@ -15,6 +16,7 @@ function normalizeHandoverId(value) {
15
16
  * and processing handover responses.
16
17
  */
17
18
  export class HandoverManager {
19
+ statefulHandover;
18
20
  event = new HandoverManagerEventEmitter();
19
21
  processName;
20
22
  processId;
@@ -31,10 +33,17 @@ export class HandoverManager {
31
33
  sourcePreparation;
32
34
  sourceExit;
33
35
  shutdownHooks;
36
+ expectedSourceId;
37
+ preparedState;
38
+ stateDigest;
39
+ acceptedStateDigest;
40
+ targetPreparation;
41
+ finRetry;
34
42
  handoverRequestEnabled = false;
35
43
  handoverEnabled = true;
36
44
  forceStartEnabled = false;
37
- constructor(processName, processId, mqttProxy, unsMqttProxies, handoverRequestEnabled, handoverEnabled, forceStartEnabled, handoverId, shutdownHooks) {
45
+ constructor(processName, processId, mqttProxy, unsMqttProxies, handoverRequestEnabled, handoverEnabled, forceStartEnabled, handoverId, shutdownHooks, statefulHandover = false) {
46
+ this.statefulHandover = statefulHandover;
38
47
  validateHandoverShutdown(shutdownHooks);
39
48
  this.shutdownHooks = shutdownHooks ? { ...shutdownHooks } : undefined;
40
49
  this.processName = processName;
@@ -51,6 +60,8 @@ export class HandoverManager {
51
60
  this.topicBuilder = new MqttTopicBuilder(`uns-infra/${MqttTopicBuilder.sanitizeTopicPart(packageName)}/${MqttTopicBuilder.sanitizeTopicPart(version)}/${MqttTopicBuilder.sanitizeTopicPart(this.processName)}/`);
52
61
  // Set status as active after a timeout if no other active process are detected.
53
62
  this.activeTimeout = setTimeout(() => {
63
+ if (this.statefulHandover && this.handoverId)
64
+ return; // A reviewed successor never self-promotes.
54
65
  logger.info(`${this.processName} - No active message received within timeout. Assuming no other process is running.`);
55
66
  this.active = true;
56
67
  this.event.emit("handoverManager", { active: this.active });
@@ -75,6 +86,7 @@ export class HandoverManager {
75
86
  const normalized = normalizeHandoverId(handoverId ?? this.handoverId);
76
87
  return JSON.stringify({
77
88
  type,
89
+ ...(this.statefulHandover ? { protocol: 2 } : {}),
78
90
  ...fields,
79
91
  ...(normalized ? { handoverId: normalized } : {}),
80
92
  });
@@ -161,6 +173,7 @@ export class HandoverManager {
161
173
  this.activeTimeout = undefined;
162
174
  this.event.emit("handoverManager", { active: this.active });
163
175
  this.requestingHandover = true;
176
+ this.expectedSourceId = sourceProcessId;
164
177
  const eventHandoverTopic = new MqttTopicBuilder(MqttTopicBuilder.extractBaseTopic(event.topic)).getHandoverTopic();
165
178
  await this.mqttProxy.publish(eventHandoverTopic, this.handoverPayload("handover_intent"), {
166
179
  retain: false,
@@ -172,7 +185,7 @@ export class HandoverManager {
172
185
  setTimeout(async () => {
173
186
  logger.info(`${this.processName} - Requesting handover ${eventHandoverTopic}.`);
174
187
  this.handoverInProgress = true;
175
- await this.mqttProxy.publish(eventHandoverTopic, this.handoverPayload("handover_request"), {
188
+ await this.mqttProxy.publish(eventHandoverTopic, this.handoverPayload("handover_request", undefined, this.statefulHandover ? { offer: this.unsMqttProxies.map(proxy => proxy.getHandoverCompatibility()) } : {}), {
176
189
  retain: false,
177
190
  properties: {
178
191
  responseTopic: this.topicBuilder.getHandoverTopic(),
@@ -231,9 +244,29 @@ export class HandoverManager {
231
244
  const mqttWorkerData = [];
232
245
  for (let i = 0; i < this.unsMqttProxies.length; i++) {
233
246
  const unsProxy = this.unsMqttProxies[i];
234
- const workerData = await unsProxy.setSubscriberPassiveAndDrainQueue();
247
+ const workerData = await unsProxy.setSubscriberPassiveAndDrainQueue(this.statefulHandover);
235
248
  mqttWorkerData.push(workerData);
236
249
  }
250
+ if (this.statefulHandover) {
251
+ // Finish accepted application callbacks before taking the calculation-state boundary.
252
+ await this.shutdownHooks?.drain();
253
+ for (const proxy of this.unsMqttProxies)
254
+ await proxy.flush();
255
+ this.preparedState = parseMqttHandoverState({ protocol: 1, proxies: this.unsMqttProxies.map((proxy, i) => proxy.exportHandoverState({ batchSize: mqttWorkerData[i].batchSize, referenceHash: mqttWorkerData[i].referenceHash })) });
256
+ this.stateDigest = mqttHandoverStateDigest(this.preparedState);
257
+ await this.sendPreparedState(event, handoverId);
258
+ let retries = 0;
259
+ this.finRetry = setInterval(() => {
260
+ if (++retries > 30 || this.sourceExit) {
261
+ clearInterval(this.finRetry);
262
+ return;
263
+ }
264
+ void this.sendPreparedState(event, handoverId).catch(() => logger.warn("MQTT handover receipt retry unavailable"));
265
+ }, 1000);
266
+ this.finRetry.unref();
267
+ // Keep the old owner passive until the successor acknowledges this exact state.
268
+ return;
269
+ }
237
270
  logger.info(`${this.processName} - Handover request accepted. Sending handover_subscriber messages.`);
238
271
  // Publish handover_subscriber messages for each instance that has processed some data.
239
272
  for (let i = 0; i < mqttWorkerData.length; i++) {
@@ -265,10 +298,48 @@ export class HandoverManager {
265
298
  logger.info(`${this.processName} - Handover fin message sent.`);
266
299
  await Promise.all(this.unsMqttProxies.map((unsProxy) => unsProxy.stop()));
267
300
  }
301
+ async sendPreparedState(event, handoverId) {
302
+ await this.mqttProxy.publish(event.packet.properties?.responseTopic ?? "", this.handoverPayload("handover_fin", handoverId, { state: this.preparedState, stateDigest: this.stateDigest }), {
303
+ retain: false, qos: 1, properties: { responseTopic: this.topicBuilder.getHandoverTopic(), userProperties: this.getUserProperties(handoverId) },
304
+ });
305
+ }
306
+ async acceptPreparedState(response) {
307
+ const state = parseMqttHandoverState(response.state);
308
+ const digest = mqttHandoverStateDigest(state);
309
+ if (digest !== response.stateDigest)
310
+ throw Error("MQTT handover state digest mismatch");
311
+ const byName = new Map(this.unsMqttProxies.map(proxy => [proxy.instanceName, proxy]));
312
+ if (byName.size !== this.unsMqttProxies.length || state.proxies.length !== byName.size || state.proxies.some(p => !byName.has(p.instanceName)))
313
+ throw Error("MQTT handover proxy set differs");
314
+ for (const snapshot of state.proxies)
315
+ byName.get(snapshot.instanceName).validateHandoverState(snapshot);
316
+ // Validate every cursor before importing any state or activating any subscriber.
317
+ for (const snapshot of state.proxies)
318
+ await byName.get(snapshot.instanceName).prepareHandoverResume(snapshot.cursor);
319
+ for (const snapshot of state.proxies)
320
+ byName.get(snapshot.instanceName).importHandoverState(snapshot);
321
+ for (const proxy of this.unsMqttProxies)
322
+ proxy.setPublisherActive();
323
+ for (const proxy of this.unsMqttProxies)
324
+ await proxy.activateHandoverResume();
325
+ this.acceptedStateDigest = digest;
326
+ this.active = true;
327
+ this.event.emit("handoverManager", { active: true });
328
+ }
268
329
  async finishSourceHandover() {
269
330
  const result = await runHandoverShutdown(async () => {
270
331
  await this.sourcePreparation;
271
332
  await this.shutdownHooks?.drain();
333
+ if (this.statefulHandover) {
334
+ await Promise.all(this.unsMqttProxies.map(proxy => proxy.stop()));
335
+ // The host persists this receipt synchronously before the real process
336
+ // exit. No receipt is emitted for a partial/failed application drain.
337
+ process.emit("uns-kit:mqtt-handover-drained", {
338
+ protocol: 2, handoverId: this.acceptedHandoverId,
339
+ stateDigest: this.stateDigest, pendingRecords: 0,
340
+ outputs: this.unsMqttProxies.length,
341
+ });
342
+ }
272
343
  }, this.shutdownHooks?.timeoutMs ?? 30_000);
273
344
  this.handoverInProgress = false;
274
345
  this.requestingHandover = false;
@@ -284,14 +355,32 @@ export class HandoverManager {
284
355
  }
285
356
  async handleHandover(event) {
286
357
  try {
358
+ if (event.message.length > 1100000)
359
+ return;
287
360
  const response = JSON.parse(event.message.toString());
361
+ // No downgrade: stateful peers must agree before either relinquishes ownership.
362
+ if ((response.protocol === 2) !== this.statefulHandover)
363
+ return;
364
+ if (this.statefulHandover && (event.packet?.retain || !this.getHandoverId(response, event)))
365
+ return;
288
366
  // Responder process
289
367
  // Check if the message is a handover request and publish MULTIPLE handover_subscriber messages
290
368
  if (response.type === "handover_request") {
291
369
  const peerId = this.getSourceProcessId(event);
292
370
  const replyTopic = event.packet?.properties?.responseTopic;
371
+ if (this.statefulHandover && this.sourcePeerId === peerId && this.acceptedHandoverId === this.getHandoverId(response, event) && this.preparedState) {
372
+ await this.sendPreparedState(event, this.acceptedHandoverId);
373
+ return;
374
+ }
293
375
  if (this.sourcePeerId || !this.active || !peerId || typeof replyTopic !== "string" || !replyTopic)
294
376
  return;
377
+ if (this.statefulHandover) {
378
+ // Reject incompatible upgrades before freezing the current owner.
379
+ const offer = mqttHandoverOfferSchema.parse(response.offer);
380
+ const own = this.unsMqttProxies.map(proxy => proxy.getHandoverCompatibility());
381
+ if (offer.length !== own.length || own.some(p => !offer.some(o => o.instanceName === p.instanceName && o.compatibility === p.compatibility)))
382
+ throw Error("MQTT source/destination or semantic contract differs; source remains active");
383
+ }
295
384
  this.sourcePeerId = peerId;
296
385
  this.acceptedHandoverId = this.getHandoverId(response, event);
297
386
  this.handoverInProgress = true;
@@ -303,8 +392,8 @@ export class HandoverManager {
303
392
  }
304
393
  catch {
305
394
  // Subscriber transfer or onRelease failed; never report successful source completion.
306
- logger.error(`${this.processName} - Handover source preparation failed. Exiting with incomplete drain.`);
307
- if (!this.sourceExit)
395
+ logger.error(`${this.processName} - Handover source preparation failed; ownership requires review.`);
396
+ if (!this.sourceExit && !this.statefulHandover)
308
397
  this.exitSource(1);
309
398
  }
310
399
  }
@@ -312,6 +401,8 @@ export class HandoverManager {
312
401
  // Check if the message is one of the handover_subscriber message in response to handover_request
313
402
  // and publish a handover_ack message
314
403
  if (response.type === "handover_subscriber") {
404
+ if (this.statefulHandover)
405
+ return;
315
406
  if (this.sourcePeerId)
316
407
  return;
317
408
  if (!this.acceptsHandoverResponse(this.getHandoverId(response, event)))
@@ -326,6 +417,20 @@ export class HandoverManager {
326
417
  // Requestor process
327
418
  // Check if the message is a handover_fin at the end of handover_subscriber messages
328
419
  if (response.type === "handover_fin") {
420
+ if (this.statefulHandover) {
421
+ const id = this.getHandoverId(response, event);
422
+ if (this.sourcePeerId || !this.requestingHandover || id !== this.handoverId || this.getSourceProcessId(event) !== this.expectedSourceId)
423
+ return;
424
+ if (this.acceptedStateDigest && response.stateDigest !== this.acceptedStateDigest)
425
+ return;
426
+ this.targetPreparation ??= this.acceptPreparedState(response);
427
+ await this.targetPreparation;
428
+ // Duplicate fin only replays the receipt; it never reapplies a baseline.
429
+ if (response.stateDigest !== this.acceptedStateDigest)
430
+ return;
431
+ await this.mqttProxy.publish(event.packet.properties?.responseTopic ?? "", this.handoverPayload("handover_ack", id, { stateDigest: this.acceptedStateDigest }), { retain: false, qos: 1, properties: { userProperties: this.getUserProperties(id) } });
432
+ return;
433
+ }
329
434
  if (this.sourcePeerId)
330
435
  return;
331
436
  const handoverId = this.getHandoverId(response, event);
@@ -363,6 +468,9 @@ export class HandoverManager {
363
468
  logger.warn(`${this.processName} - Ignoring acknowledgement for another migration.`);
364
469
  return;
365
470
  }
471
+ if (this.statefulHandover && (handoverId !== this.acceptedHandoverId || response.stateDigest !== this.stateDigest || !this.stateDigest))
472
+ return;
473
+ clearInterval(this.finRetry);
366
474
  logger.info(`${this.processName} - Received handover ack from ${event.packet?.properties?.userProperties?.processName}.`);
367
475
  this.sourceExit ??= this.finishSourceHandover();
368
476
  await this.sourceExit;