@uns-kit/core 3.0.27 → 3.0.30

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 (103) hide show
  1. package/MIGRATIONS.md +122 -0
  2. package/README.md +10 -0
  3. package/dist/config/app-config.d.ts +14 -0
  4. package/dist/config/app-config.d.ts.map +1 -1
  5. package/dist/config/app-config.js.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/runtime/configuration-group-authorization.d.ts +50 -0
  11. package/dist/runtime/configuration-group-authorization.d.ts.map +1 -0
  12. package/dist/runtime/configuration-group-authorization.js +113 -0
  13. package/dist/runtime/configuration-group-authorization.js.map +1 -0
  14. package/dist/runtime/configuration-group-contract.d.ts +75 -0
  15. package/dist/runtime/configuration-group-contract.d.ts.map +1 -0
  16. package/dist/runtime/configuration-group-contract.js +233 -0
  17. package/dist/runtime/configuration-group-contract.js.map +1 -0
  18. package/dist/runtime/process-group-authorization.d.ts +6 -0
  19. package/dist/runtime/process-group-authorization.d.ts.map +1 -1
  20. package/dist/runtime/process-group-authorization.js +56 -16
  21. package/dist/runtime/process-group-authorization.js.map +1 -1
  22. package/dist/runtime/process-group-backup-contract.d.ts +22 -0
  23. package/dist/runtime/process-group-backup-contract.d.ts.map +1 -0
  24. package/dist/runtime/process-group-backup-contract.js +20 -0
  25. package/dist/runtime/process-group-backup-contract.js.map +1 -0
  26. package/dist/runtime/process-group-backup-contract.json +7 -0
  27. package/dist/runtime/process-group-configured-upgrade.d.ts +4 -0
  28. package/dist/runtime/process-group-configured-upgrade.d.ts.map +1 -0
  29. package/dist/runtime/process-group-configured-upgrade.js +107 -0
  30. package/dist/runtime/process-group-configured-upgrade.js.map +1 -0
  31. package/dist/runtime/process-group-generation.d.ts +166 -0
  32. package/dist/runtime/process-group-generation.d.ts.map +1 -0
  33. package/dist/runtime/process-group-generation.js +278 -0
  34. package/dist/runtime/process-group-generation.js.map +1 -0
  35. package/dist/runtime/process-group-inspection.d.ts +2 -0
  36. package/dist/runtime/process-group-inspection.d.ts.map +1 -1
  37. package/dist/runtime/process-group-inspection.js +120 -40
  38. package/dist/runtime/process-group-inspection.js.map +1 -1
  39. package/dist/runtime/process-group-resources.d.ts +1 -0
  40. package/dist/runtime/process-group-resources.d.ts.map +1 -1
  41. package/dist/runtime/process-group-resources.js +8 -0
  42. package/dist/runtime/process-group-resources.js.map +1 -1
  43. package/dist/runtime/process-group-runtime-selection.d.ts +14 -0
  44. package/dist/runtime/process-group-runtime-selection.d.ts.map +1 -0
  45. package/dist/runtime/process-group-runtime-selection.js +29 -0
  46. package/dist/runtime/process-group-runtime-selection.js.map +1 -0
  47. package/dist/runtime/process-group-snapshot.mjs +60 -8
  48. package/dist/runtime/process-group-staging.d.ts +2 -2
  49. package/dist/runtime/process-group-staging.d.ts.map +1 -1
  50. package/dist/runtime/process-group-staging.js +6 -4
  51. package/dist/runtime/process-group-staging.js.map +1 -1
  52. package/dist/runtime/process-group-supervision.d.ts +2 -2
  53. package/dist/runtime/process-group-supervision.d.ts.map +1 -1
  54. package/dist/runtime/process-group-supervision.js +5 -3
  55. package/dist/runtime/process-group-supervision.js.map +1 -1
  56. package/dist/runtime/process-group-upgrade.d.ts +195 -0
  57. package/dist/runtime/process-group-upgrade.d.ts.map +1 -0
  58. package/dist/runtime/process-group-upgrade.js +262 -0
  59. package/dist/runtime/process-group-upgrade.js.map +1 -0
  60. package/dist/uns/uns-interfaces.d.ts +3 -0
  61. package/dist/uns/uns-interfaces.d.ts.map +1 -1
  62. package/dist/uns/uns-interfaces.js.map +1 -1
  63. package/dist/uns/uns-proxy-process.d.ts.map +1 -1
  64. package/dist/uns/uns-proxy-process.js +3 -0
  65. package/dist/uns/uns-proxy-process.js.map +1 -1
  66. package/dist/uns-config/config-schema.d.ts +16 -0
  67. package/dist/uns-config/config-schema.d.ts.map +1 -1
  68. package/dist/uns-config/mqtt-channel.d.ts +1 -1
  69. package/dist/uns-config/mqtt-channel.d.ts.map +1 -1
  70. package/dist/uns-config/mqtt-channel.js +3 -0
  71. package/dist/uns-config/mqtt-channel.js.map +1 -1
  72. package/dist/uns-config/uns-core-schema.d.ts +137 -0
  73. package/dist/uns-config/uns-core-schema.d.ts.map +1 -1
  74. package/dist/uns-config/uns-core-schema.js +6 -1
  75. package/dist/uns-config/uns-core-schema.js.map +1 -1
  76. package/dist/uns-mqtt/mqtt-interfaces.d.ts +5 -0
  77. package/dist/uns-mqtt/mqtt-interfaces.d.ts.map +1 -1
  78. package/dist/uns-mqtt/mqtt-interfaces.js.map +1 -1
  79. package/dist/uns-mqtt/mqtt-proxy.d.ts +3 -0
  80. package/dist/uns-mqtt/mqtt-proxy.d.ts.map +1 -1
  81. package/dist/uns-mqtt/mqtt-proxy.js +26 -1
  82. package/dist/uns-mqtt/mqtt-proxy.js.map +1 -1
  83. package/dist/uns-mqtt/mqtt-worker.d.ts +3 -1
  84. package/dist/uns-mqtt/mqtt-worker.d.ts.map +1 -1
  85. package/dist/uns-mqtt/mqtt-worker.js +45 -8
  86. package/dist/uns-mqtt/mqtt-worker.js.map +1 -1
  87. package/dist/uns-mqtt/publish-queue-contract.d.ts +33 -0
  88. package/dist/uns-mqtt/publish-queue-contract.d.ts.map +1 -0
  89. package/dist/uns-mqtt/publish-queue-contract.js +18 -0
  90. package/dist/uns-mqtt/publish-queue-contract.js.map +1 -0
  91. package/dist/uns-mqtt/publish-queue-store.d.ts +33 -0
  92. package/dist/uns-mqtt/publish-queue-store.d.ts.map +1 -0
  93. package/dist/uns-mqtt/publish-queue-store.js +177 -0
  94. package/dist/uns-mqtt/publish-queue-store.js.map +1 -0
  95. package/dist/uns-mqtt/publish-queue.d.ts +43 -0
  96. package/dist/uns-mqtt/publish-queue.d.ts.map +1 -0
  97. package/dist/uns-mqtt/publish-queue.js +235 -0
  98. package/dist/uns-mqtt/publish-queue.js.map +1 -0
  99. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts +7 -0
  100. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts.map +1 -1
  101. package/dist/uns-mqtt/uns-mqtt-proxy.js +98 -5
  102. package/dist/uns-mqtt/uns-mqtt-proxy.js.map +1 -1
  103. package/package.json +1 -1
@@ -0,0 +1,233 @@
1
+ import { createHash } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { z } from "zod";
5
+ import { readConfigurationSelector } from "./process-group-contract.js";
6
+ import { readProcessGroupResource } from "./process-group-resources.js";
7
+ const identity = z.string().regex(/^[a-z][a-z0-9-]{0,99}$/);
8
+ const digest = z.string().regex(/^[a-f0-9]{64}$/);
9
+ const selector = z
10
+ .array(z
11
+ .string()
12
+ .regex(/^[a-zA-Z][a-zA-Z0-9_-]{0,99}$/)
13
+ .refine((v) => !["__proto__", "prototype", "constructor"].includes(v)))
14
+ .min(1)
15
+ .max(8);
16
+ const MAX_BYTES = 1024 * 1024;
17
+ /** Versioned canonical JSON: binary key order, no locale, executable values or accessors. */
18
+ export function configurationResourceJson(value) {
19
+ let nodes = 0, bytes = 0;
20
+ const ancestors = new Set();
21
+ const charge = (size) => {
22
+ bytes += size;
23
+ if (bytes > MAX_BYTES)
24
+ throw Error("Configuration resource exceeds 1 MiB");
25
+ };
26
+ function visit(v, depth) {
27
+ if (++nodes > 65536 || depth > 32)
28
+ throw Error("Configuration resource exceeds its structural budget");
29
+ if (v === null || typeof v === "boolean") {
30
+ charge(v === null ? 4 : v ? 4 : 5);
31
+ return JSON.stringify(v);
32
+ }
33
+ if (typeof v === "string") {
34
+ if (Buffer.byteLength(v) > MAX_BYTES)
35
+ throw Error("Configuration resource string exceeds 1 MiB");
36
+ const encoded = JSON.stringify(v);
37
+ charge(Buffer.byteLength(encoded));
38
+ return encoded;
39
+ }
40
+ if (typeof v === "number" && Number.isFinite(v)) {
41
+ const encoded = JSON.stringify(v);
42
+ charge(encoded.length);
43
+ return encoded;
44
+ }
45
+ if (!v || typeof v !== "object" || ancestors.has(v))
46
+ throw Error("Configuration resource must contain finite plain JSON only");
47
+ if (Array.isArray(v)
48
+ ? Object.getPrototypeOf(v) !== Array.prototype
49
+ : Object.getPrototypeOf(v) !== Object.prototype && Object.getPrototypeOf(v) !== null)
50
+ throw Error("Configuration resource must contain plain JSON objects");
51
+ if (Object.getOwnPropertySymbols(v).length)
52
+ throw Error("Configuration resource cannot contain symbol properties");
53
+ const descriptors = Object.getOwnPropertyDescriptors(v);
54
+ for (const [key, d] of Object.entries(descriptors)) {
55
+ if (Array.isArray(v) && key === "length")
56
+ continue;
57
+ if (!d.enumerable || !Object.hasOwn(d, "value"))
58
+ throw Error("Configuration resource cannot contain hidden properties or accessors");
59
+ }
60
+ ancestors.add(v);
61
+ charge(2); // Container brackets/braces.
62
+ let result;
63
+ if (Array.isArray(v)) {
64
+ if (v.length > 65536 || Object.keys(v).length !== v.length || Object.keys(v).some((k, i) => k !== String(i)))
65
+ throw Error("Configuration resource arrays must be dense JSON arrays");
66
+ charge(Math.max(0, v.length - 1));
67
+ result = "[" + Array.from({ length: v.length }, (_, i) => visit(descriptors[String(i)].value, depth + 1)).join(",") + "]";
68
+ }
69
+ else
70
+ result =
71
+ "{" +
72
+ Object.keys(v)
73
+ .sort()
74
+ .map((k, i) => {
75
+ if (Buffer.byteLength(k) > MAX_BYTES)
76
+ throw Error("Configuration resource key exceeds 1 MiB");
77
+ charge(Buffer.byteLength(JSON.stringify(k)) + 1 + (i ? 1 : 0));
78
+ return JSON.stringify(k) + ":" + visit(descriptors[k].value, depth + 1);
79
+ })
80
+ .join(",") +
81
+ "}";
82
+ ancestors.delete(v);
83
+ return result;
84
+ }
85
+ const json = visit(value, 0);
86
+ if (Buffer.byteLength(json) > MAX_BYTES)
87
+ throw Error("Configuration resource exceeds 1 MiB");
88
+ return json;
89
+ }
90
+ export function configurationResourceHash(value) {
91
+ return createHash("sha256").update("uns-configuration-json-v1\n").update(configurationResourceJson(value)).digest("hex");
92
+ }
93
+ export const configurationGroupProfileSchema = z.strictObject({
94
+ id: identity,
95
+ processName: identity,
96
+ dataGroup: identity,
97
+ resource: z.unknown(),
98
+ });
99
+ export const configurationGroupSchema = z.strictObject({
100
+ schemaVersion: z.literal(2),
101
+ id: identity,
102
+ inputTopic: z
103
+ .string()
104
+ .min(1)
105
+ .max(512)
106
+ .regex(/^[^#+\0]+$/),
107
+ processName: identity,
108
+ receiverProcessName: identity,
109
+ profiles: z.array(configurationGroupProfileSchema).min(2).max(16),
110
+ });
111
+ export const configurationGroupDescriptorSchema = z
112
+ .strictObject({
113
+ protocolVersion: z.literal(2),
114
+ namespace: identity,
115
+ publisherPath: selector,
116
+ groupPath: selector,
117
+ memberPath: selector,
118
+ journalPath: selector,
119
+ runtimeRolePath: selector,
120
+ receiverRole: identity,
121
+ match: z.strictObject({ path: selector, value: z.string().min(1).max(100) }).optional(),
122
+ })
123
+ .superRefine((d, context) => {
124
+ const independent = [d.publisherPath, d.journalPath, d.runtimeRolePath].map((p) => p.join("."));
125
+ if (new Set(independent).size !== independent.length || independent.some((p, i) => independent.some((q, j) => i !== j && q.startsWith(p + "."))))
126
+ context.addIssue({ code: "custom", message: "Independent publisher, journal and role selectors required" });
127
+ const parent = d.publisherPath.join(".") + ".", group = d.groupPath.join("."), member = d.memberPath.join(".");
128
+ if (!group.startsWith(parent) ||
129
+ !member.startsWith(parent) ||
130
+ group === member ||
131
+ group.startsWith(member + ".") ||
132
+ member.startsWith(group + "."))
133
+ context.addIssue({ code: "custom", message: "Distinct group/member selectors within the publisher required" });
134
+ });
135
+ const snapshots = new WeakSet();
136
+ function freeze(value) {
137
+ if (value && typeof value === "object") {
138
+ for (const child of Object.values(value))
139
+ freeze(child);
140
+ Object.freeze(value);
141
+ }
142
+ return value;
143
+ }
144
+ export function configurationGroupSnapshot(value) {
145
+ // Bound and copy before schema traversal; caller mutation cannot change a running revision.
146
+ const group = configurationGroupSchema.parse(JSON.parse(configurationResourceJson(value)));
147
+ const names = [group.processName, group.receiverProcessName, ...group.profiles.map((p) => p.processName)];
148
+ if (new Set(names).size !== names.length || new Set(group.profiles.map((p) => p.id)).size !== group.profiles.length)
149
+ throw Error("Configuration group identities overlap");
150
+ const profiles = group.profiles.map((p) => ({ ...p, resourceSha256: configurationResourceHash(p.resource) }));
151
+ const result = freeze({ group, sha256: configurationResourceHash(group), profiles });
152
+ snapshots.add(result);
153
+ return result;
154
+ }
155
+ export function assertConfigurationGroupSnapshot(value) {
156
+ if (!snapshots.has(value))
157
+ throw Error("Exact SDK-issued configuration group snapshot required");
158
+ }
159
+ /** Optional data-only capability; packages without it keep their existing behavior. */
160
+ export function configurationGroupDescriptor(packageRoot) {
161
+ const file = path.join(packageRoot, "package.json");
162
+ // Ordinary Node/Python deployments do not opt in by existing on the host.
163
+ if (!fs.existsSync(file))
164
+ return undefined;
165
+ const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
166
+ let bytes;
167
+ try {
168
+ const stat = fs.fstatSync(fd);
169
+ if (!stat.isFile() || stat.size > MAX_BYTES)
170
+ throw Error("Invalid installed runtime metadata");
171
+ bytes = fs.readFileSync(fd);
172
+ if (bytes.length > MAX_BYTES)
173
+ throw Error("Installed runtime metadata is too large");
174
+ }
175
+ finally {
176
+ fs.closeSync(fd);
177
+ }
178
+ const pkg = JSON.parse(bytes.toString());
179
+ const value = pkg.unsDatahub?.processGroupConfigurationRuntime;
180
+ if (value !== undefined && !readProcessGroupResource(file).equals(bytes))
181
+ throw Error("Installed runtime metadata changed during discovery");
182
+ return value === undefined ? undefined : configurationGroupDescriptorSchema.parse(value);
183
+ }
184
+ export function selectedConfigurationGroup(packageRoot, raw) {
185
+ const descriptor = configurationGroupDescriptor(packageRoot);
186
+ if (!descriptor)
187
+ return undefined;
188
+ const value = readConfigurationSelector(raw, descriptor.groupPath);
189
+ if (value === undefined)
190
+ return undefined;
191
+ if (descriptor.match && readConfigurationSelector(raw, descriptor.match.path) !== descriptor.match.value)
192
+ throw Error("Configuration group capability selector does not match");
193
+ return { descriptor, snapshot: configurationGroupSnapshot(value) };
194
+ }
195
+ export const configurationGroupJournalSchema = z.strictObject({
196
+ directory: z.string().startsWith("/").max(512),
197
+ journalId: z.string().uuid().optional(),
198
+ generationId: identity,
199
+ groupSha256: digest,
200
+ minFreeBytes: z
201
+ .number()
202
+ .int()
203
+ .min(1024 * 1024)
204
+ .max(1024 * 1024 * 1024)
205
+ .default(64 * 1024 * 1024),
206
+ maxRecords: z.number().int().min(1).max(100000).default(20000),
207
+ maxBytes: z
208
+ .number()
209
+ .int()
210
+ .min(1024)
211
+ .max(256 * 1024 * 1024)
212
+ .default(32 * 1024 * 1024),
213
+ });
214
+ export function configurationGroupJournalContract(snapshot, sourceIdentity, config, profiles) {
215
+ assertConfigurationGroupSnapshot(snapshot);
216
+ const journal = configurationGroupJournalSchema.parse(config);
217
+ if (journal.groupSha256 !== snapshot.sha256 || !digest.safeParse(sourceIdentity).success)
218
+ throw Error("Configuration group journal revision differs");
219
+ const expected = snapshot.profiles.map(({ processName, resourceSha256, dataGroup }) => ({ processName, resourceSha256, dataGroup }));
220
+ const normalize = (v) => [...v].sort((a, b) => (a.processName < b.processName ? -1 : a.processName > b.processName ? 1 : 0));
221
+ if (configurationResourceHash(normalize([...profiles])) !== configurationResourceHash(normalize(expected)))
222
+ throw Error("Configuration group journal profiles differ");
223
+ return configurationResourceJson({
224
+ version: 2,
225
+ generationId: journal.generationId,
226
+ groupSha256: snapshot.sha256,
227
+ sourceIdentity,
228
+ maxRecords: journal.maxRecords,
229
+ maxBytes: journal.maxBytes,
230
+ profiles: normalize(expected),
231
+ });
232
+ }
233
+ //# sourceMappingURL=configuration-group-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configuration-group-contract.js","sourceRoot":"","sources":["../../src/runtime/configuration-group-contract.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { readConfigurationSelector } from \"./process-group-contract.js\";\nimport { readProcessGroupResource } from \"./process-group-resources.js\";\n\nconst identity = z.string().regex(/^[a-z][a-z0-9-]{0,99}$/);\nconst digest = z.string().regex(/^[a-f0-9]{64}$/);\nconst selector = z\n .array(\n z\n .string()\n .regex(/^[a-zA-Z][a-zA-Z0-9_-]{0,99}$/)\n .refine((v) => ![\"__proto__\", \"prototype\", \"constructor\"].includes(v)),\n )\n .min(1)\n .max(8);\nconst MAX_BYTES = 1024 * 1024;\n\n/** Versioned canonical JSON: binary key order, no locale, executable values or accessors. */\nexport function configurationResourceJson(value: unknown): string {\n let nodes = 0,\n bytes = 0;\n const ancestors = new Set<object>();\n const charge = (size: number) => {\n bytes += size;\n if (bytes > MAX_BYTES) throw Error(\"Configuration resource exceeds 1 MiB\");\n };\n function visit(v: unknown, depth: number): string {\n if (++nodes > 65536 || depth > 32) throw Error(\"Configuration resource exceeds its structural budget\");\n if (v === null || typeof v === \"boolean\") {\n charge(v === null ? 4 : v ? 4 : 5);\n return JSON.stringify(v);\n }\n if (typeof v === \"string\") {\n if (Buffer.byteLength(v) > MAX_BYTES) throw Error(\"Configuration resource string exceeds 1 MiB\");\n const encoded = JSON.stringify(v);\n charge(Buffer.byteLength(encoded));\n return encoded;\n }\n if (typeof v === \"number\" && Number.isFinite(v)) {\n const encoded = JSON.stringify(v);\n charge(encoded.length);\n return encoded;\n }\n if (!v || typeof v !== \"object\" || ancestors.has(v)) throw Error(\"Configuration resource must contain finite plain JSON only\");\n if (\n Array.isArray(v)\n ? Object.getPrototypeOf(v) !== Array.prototype\n : Object.getPrototypeOf(v) !== Object.prototype && Object.getPrototypeOf(v) !== null\n )\n throw Error(\"Configuration resource must contain plain JSON objects\");\n if (Object.getOwnPropertySymbols(v).length) throw Error(\"Configuration resource cannot contain symbol properties\");\n const descriptors = Object.getOwnPropertyDescriptors(v);\n for (const [key, d] of Object.entries(descriptors)) {\n if (Array.isArray(v) && key === \"length\") continue;\n if (!d.enumerable || !Object.hasOwn(d, \"value\")) throw Error(\"Configuration resource cannot contain hidden properties or accessors\");\n }\n ancestors.add(v);\n charge(2); // Container brackets/braces.\n let result: string;\n if (Array.isArray(v)) {\n if (v.length > 65536 || Object.keys(v).length !== v.length || Object.keys(v).some((k, i) => k !== String(i)))\n throw Error(\"Configuration resource arrays must be dense JSON arrays\");\n charge(Math.max(0, v.length - 1));\n result = \"[\" + Array.from({ length: v.length }, (_, i) => visit(descriptors[String(i)]!.value, depth + 1)).join(\",\") + \"]\";\n } else\n result =\n \"{\" +\n Object.keys(v)\n .sort()\n .map((k, i) => {\n if (Buffer.byteLength(k) > MAX_BYTES) throw Error(\"Configuration resource key exceeds 1 MiB\");\n charge(Buffer.byteLength(JSON.stringify(k)) + 1 + (i ? 1 : 0));\n return JSON.stringify(k) + \":\" + visit(descriptors[k]!.value, depth + 1);\n })\n .join(\",\") +\n \"}\";\n ancestors.delete(v);\n return result;\n }\n const json = visit(value, 0);\n if (Buffer.byteLength(json) > MAX_BYTES) throw Error(\"Configuration resource exceeds 1 MiB\");\n return json;\n}\n\nexport function configurationResourceHash(value: unknown): string {\n return createHash(\"sha256\").update(\"uns-configuration-json-v1\\n\").update(configurationResourceJson(value)).digest(\"hex\");\n}\n\nexport const configurationGroupProfileSchema = z.strictObject({\n id: identity,\n processName: identity,\n dataGroup: identity,\n resource: z.unknown(),\n});\nexport const configurationGroupSchema = z.strictObject({\n schemaVersion: z.literal(2),\n id: identity,\n inputTopic: z\n .string()\n .min(1)\n .max(512)\n .regex(/^[^#+\\0]+$/),\n processName: identity,\n receiverProcessName: identity,\n profiles: z.array(configurationGroupProfileSchema).min(2).max(16),\n});\nexport const configurationGroupDescriptorSchema = z\n .strictObject({\n protocolVersion: z.literal(2),\n namespace: identity,\n publisherPath: selector,\n groupPath: selector,\n memberPath: selector,\n journalPath: selector,\n runtimeRolePath: selector,\n receiverRole: identity,\n match: z.strictObject({ path: selector, value: z.string().min(1).max(100) }).optional(),\n })\n .superRefine((d, context) => {\n const independent = [d.publisherPath, d.journalPath, d.runtimeRolePath].map((p) => p.join(\".\"));\n if (new Set(independent).size !== independent.length || independent.some((p, i) => independent.some((q, j) => i !== j && q.startsWith(p + \".\"))))\n context.addIssue({ code: \"custom\", message: \"Independent publisher, journal and role selectors required\" });\n const parent = d.publisherPath.join(\".\") + \".\",\n group = d.groupPath.join(\".\"),\n member = d.memberPath.join(\".\");\n if (\n !group.startsWith(parent) ||\n !member.startsWith(parent) ||\n group === member ||\n group.startsWith(member + \".\") ||\n member.startsWith(group + \".\")\n )\n context.addIssue({ code: \"custom\", message: \"Distinct group/member selectors within the publisher required\" });\n });\nexport type ConfigurationGroupDescriptor = z.infer<typeof configurationGroupDescriptorSchema>;\nexport type ConfigurationGroup = z.infer<typeof configurationGroupSchema>;\nexport interface ConfigurationGroupProfile {\n readonly id: string;\n readonly processName: string;\n readonly dataGroup: string;\n readonly resourceSha256: string;\n readonly resource: unknown;\n}\nexport interface ConfigurationGroupSnapshot {\n readonly group: Readonly<ConfigurationGroup>;\n readonly sha256: string;\n readonly profiles: readonly ConfigurationGroupProfile[];\n}\nconst snapshots = new WeakSet<object>();\nfunction freeze<T>(value: T): T {\n if (value && typeof value === \"object\") {\n for (const child of Object.values(value)) freeze(child);\n Object.freeze(value);\n }\n return value;\n}\nexport function configurationGroupSnapshot(value: unknown): ConfigurationGroupSnapshot {\n // Bound and copy before schema traversal; caller mutation cannot change a running revision.\n const group = configurationGroupSchema.parse(JSON.parse(configurationResourceJson(value)));\n const names = [group.processName, group.receiverProcessName, ...group.profiles.map((p) => p.processName)];\n if (new Set(names).size !== names.length || new Set(group.profiles.map((p) => p.id)).size !== group.profiles.length)\n throw Error(\"Configuration group identities overlap\");\n const profiles = group.profiles.map((p) => ({ ...p, resourceSha256: configurationResourceHash(p.resource) }));\n const result = freeze({ group, sha256: configurationResourceHash(group), profiles });\n snapshots.add(result);\n return result;\n}\nexport function assertConfigurationGroupSnapshot(value: ConfigurationGroupSnapshot): void {\n if (!snapshots.has(value)) throw Error(\"Exact SDK-issued configuration group snapshot required\");\n}\n\n/** Optional data-only capability; packages without it keep their existing behavior. */\nexport function configurationGroupDescriptor(packageRoot: string): ConfigurationGroupDescriptor | undefined {\n const file = path.join(packageRoot, \"package.json\");\n // Ordinary Node/Python deployments do not opt in by existing on the host.\n if (!fs.existsSync(file)) return undefined;\n const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);\n let bytes: Buffer;\n try {\n const stat = fs.fstatSync(fd);\n if (!stat.isFile() || stat.size > MAX_BYTES) throw Error(\"Invalid installed runtime metadata\");\n bytes = fs.readFileSync(fd);\n if (bytes.length > MAX_BYTES) throw Error(\"Installed runtime metadata is too large\");\n } finally {\n fs.closeSync(fd);\n }\n const pkg = JSON.parse(bytes.toString());\n const value = pkg.unsDatahub?.processGroupConfigurationRuntime;\n if (value !== undefined && !readProcessGroupResource(file).equals(bytes)) throw Error(\"Installed runtime metadata changed during discovery\");\n return value === undefined ? undefined : configurationGroupDescriptorSchema.parse(value);\n}\nexport function selectedConfigurationGroup(\n packageRoot: string,\n raw: Record<string, unknown>,\n):\n | {\n descriptor: ConfigurationGroupDescriptor;\n snapshot: ConfigurationGroupSnapshot;\n }\n | undefined {\n const descriptor = configurationGroupDescriptor(packageRoot);\n if (!descriptor) return undefined;\n const value = readConfigurationSelector(raw, descriptor.groupPath);\n if (value === undefined) return undefined;\n if (descriptor.match && readConfigurationSelector(raw, descriptor.match.path) !== descriptor.match.value)\n throw Error(\"Configuration group capability selector does not match\");\n return { descriptor, snapshot: configurationGroupSnapshot(value) };\n}\n\nexport const configurationGroupJournalSchema = z.strictObject({\n directory: z.string().startsWith(\"/\").max(512),\n journalId: z.string().uuid().optional(),\n generationId: identity,\n groupSha256: digest,\n minFreeBytes: z\n .number()\n .int()\n .min(1024 * 1024)\n .max(1024 * 1024 * 1024)\n .default(64 * 1024 * 1024),\n maxRecords: z.number().int().min(1).max(100000).default(20000),\n maxBytes: z\n .number()\n .int()\n .min(1024)\n .max(256 * 1024 * 1024)\n .default(32 * 1024 * 1024),\n});\nexport type ConfigurationGroupJournal = z.infer<typeof configurationGroupJournalSchema>;\nexport function configurationGroupJournalContract(\n snapshot: ConfigurationGroupSnapshot,\n sourceIdentity: string,\n config: ConfigurationGroupJournal,\n profiles: readonly { processName: string; resourceSha256: string; dataGroup: string }[],\n): string {\n assertConfigurationGroupSnapshot(snapshot);\n const journal = configurationGroupJournalSchema.parse(config);\n if (journal.groupSha256 !== snapshot.sha256 || !digest.safeParse(sourceIdentity).success)\n throw Error(\"Configuration group journal revision differs\");\n const expected = snapshot.profiles.map(({ processName, resourceSha256, dataGroup }) => ({ processName, resourceSha256, dataGroup }));\n const normalize = (v: typeof expected) => [...v].sort((a, b) => (a.processName < b.processName ? -1 : a.processName > b.processName ? 1 : 0));\n if (configurationResourceHash(normalize([...profiles])) !== configurationResourceHash(normalize(expected)))\n throw Error(\"Configuration group journal profiles differ\");\n return configurationResourceJson({\n version: 2,\n generationId: journal.generationId,\n groupSha256: snapshot.sha256,\n sourceIdentity,\n maxRecords: journal.maxRecords,\n maxBytes: journal.maxBytes,\n profiles: normalize(expected),\n });\n}\n"]}
@@ -52,6 +52,8 @@ interface GenericAuthorization {
52
52
  readonly groupId: string;
53
53
  readonly groupProcessName: string;
54
54
  readonly namespace: string;
55
+ readonly generationId?: string;
56
+ readonly configurationGroupSha256?: string;
55
57
  }
56
58
  export type ProcessGroupAuthorization = legacy.HvAuthorization | GenericAuthorization;
57
59
  export type HvAuthorization = ProcessGroupAuthorization;
@@ -95,7 +97,11 @@ export interface ProcessGroupAuthorizationContext {
95
97
  stateRoot?: string;
96
98
  initialize?: boolean;
97
99
  }
100
+ /** Live authority always comes from the current canonical private policy. */
98
101
  export declare function authorizeProcessGroupRuntime(context: ProcessGroupAuthorizationContext): ProcessGroupAuthorization;
102
+ /** Review only: never issues an authorization accepted by runtime/launch checks. */
103
+ export declare function validateProcessGroupPolicyCandidate(context: ProcessGroupAuthorizationContext, bytes: Buffer): void;
104
+ export declare function processGroupAuthorizationFile(context: ProcessGroupAuthorizationContext): string;
99
105
  export declare const authorizeHvRuntime: typeof authorizeProcessGroupRuntime;
100
106
  export declare function authorizeConfiguredProcessGroupRuntime(pkg: string, dir: string, raw: Record<string, unknown>, controllerName: string, initialize?: boolean): ProcessGroupAuthorization;
101
107
  export declare function assertProcessGroupJournalAuthorization(value: ProcessGroupAuthorization | undefined, directory: string, journalId: string | undefined): void;
@@ -1 +1 @@
1
- {"version":3,"file":"process-group-authorization.d.ts","sourceRoot":"","sources":["../../src/runtime/process-group-authorization.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,MAAM,MAAM,+CAA+C,CAAC;AAexE,OAAO,EAAE,8BAA8B,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEpH,eAAO,MAAM,4BAA4B,4CAAsC,CAAC;AAahF,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;kBAiB1C,CAAC;AACH,MAAM,MAAM,+BAA+B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAC9F,eAAO,MAAM,0BAA0B;;;;;;;;;;;;kBAarC,CAAC;AACH,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AACD,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,eAAe,GAAG,oBAAoB,CAAC;AACtF,MAAM,MAAM,eAAe,GAAG,yBAAyB,CAAC;AACxD,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,qBAAqB,CAAC;AACjE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;kBAA+B,CAAC;AAClE,eAAO,MAAM,kBAAkB,8BAA4B,CAAC;AAsC5D;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,GAAG,CAAC,MAAM,CAAC,CAgFzD;AACD,MAAM,WAAW,gCAAgC;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AACD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gCAAgC,GAAG,yBAAyB,CAkGjH;AACD,eAAO,MAAM,kBAAkB,qCAA+B,CAAC;AAC/D,wBAAgB,sCAAsC,CACpD,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,cAAc,EAAE,MAAM,EACtB,UAAU,UAAQ,6BAUnB;AACD,wBAAgB,sCAAsC,CACpD,KAAK,EAAE,yBAAyB,GAAG,SAAS,EAC5C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,IAAI,CAON;AACD,eAAO,MAAM,4BAA4B,+CAAyC,CAAC;AACnF,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,yBAAyB,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAgBjG;AACD,eAAO,MAAM,kBAAkB,qCAA+B,CAAC;AAC/D,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,yBAAyB,EAChC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,IAAI,CAsBN;AACD,eAAO,MAAM,oBAAoB,uCAAiC,CAAC"}
1
+ {"version":3,"file":"process-group-authorization.d.ts","sourceRoot":"","sources":["../../src/runtime/process-group-authorization.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,9 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { z } from "zod";
4
4
  import * as legacy from "./compatibility/energenti-authorization-v1.js";
5
+ import { configurationGroupAuthorizationSchema, configurationGroupPolicyIdentity, verifyConfigurationGroupPolicy } from "./configuration-group-authorization.js";
5
6
  import { normalizeProcessGroupConfiguration, processGroupDescriptor, resolvedProcessGroupChannels, } from "./process-group-contract.js";
6
- import { configurationAuthorizationHash, groupArtifactHash, groupChannelsHash, readProcessGroupResource as read, sha256 as sha, } from "./process-group-resources.js";
7
+ import { configurationAuthorizationHash, groupArtifactHash, groupChannelsHash, readProcessGroupResource as read, sha256 as sha, assertPrivateProcessGroupDirectory, } from "./process-group-resources.js";
7
8
  export { configurationAuthorizationHash, groupArtifactHash, groupChannelsHash } from "./process-group-resources.js";
8
9
  export const hasProcessGroupAuthorization = legacy.hasProcessGroupAuthorization;
9
10
  const digest = z.string().regex(/^[a-f0-9]{64}$/), id = z.string().regex(/^[a-z][a-z0-9-]{0,99}$/);
@@ -52,14 +53,7 @@ export const processGroupManifestSchema = z.strictObject({
52
53
  export const hvAuthorizationSchema = legacy.hvAuthorizationSchema;
53
54
  export const productionGroupIds = legacy.productionGroupIds;
54
55
  const issued = new WeakSet();
55
- function privateDirectory(dir) {
56
- for (let p = dir; p !== path.dirname(p); p = path.dirname(p))
57
- if (fs.lstatSync(p).isSymbolicLink())
58
- throw Error("Linked policy directory");
59
- const s = fs.lstatSync(dir);
60
- if (!s.isDirectory() || s.uid !== process.getuid?.() || (s.mode & 0o077) !== 0)
61
- throw Error("Insecure policy directory");
62
- }
56
+ const privateDirectory = assertPrivateProcessGroupDirectory;
63
57
  function registryRoot(root, dir) {
64
58
  if (!path.isAbsolute(root) || path.resolve(root) !== root || path.resolve(dir) !== dir)
65
59
  throw Error("Invalid policy registry");
@@ -74,6 +68,24 @@ function registryRoot(root, dir) {
74
68
  return relative.split("/")[0];
75
69
  }
76
70
  function selectedFile(pkg, raw, root, explicit) {
71
+ const configured = configurationGroupPolicyIdentity(pkg, raw);
72
+ if (configured) {
73
+ if (explicit)
74
+ return explicit;
75
+ const directory = process.env["UNS_PROCESS_GROUP_AUTHORIZATION_DIRECTORY"];
76
+ if (directory) {
77
+ const selected = registryRoot(root, directory);
78
+ if (selected !== "*" && selected !== configured.namespace)
79
+ throw Error("Foreign runtime registry");
80
+ const registry = selected === "*" ? path.join(root, configured.namespace, "authorizations") : directory;
81
+ privateDirectory(registry);
82
+ return path.join(registry, configured.generationId + ".json");
83
+ }
84
+ const single = process.env["UNS_PROCESS_GROUP_AUTHORIZATION_FILE"];
85
+ if (!single)
86
+ throw Error("No selected policy");
87
+ return single;
88
+ }
77
89
  const descriptor = processGroupDescriptor(pkg);
78
90
  if (!descriptor)
79
91
  throw Error("Missing runtime contract");
@@ -175,10 +187,11 @@ export function configuredGroupProcessNames() {
175
187
  const result = new Set();
176
188
  for (const file of files) {
177
189
  const raw = JSON.parse(read(file, true).toString());
178
- const p = raw.schemaVersion === 3
190
+ const p = raw.schemaVersion === 4 ? configurationGroupAuthorizationSchema.parse(raw) : raw.schemaVersion === 3
179
191
  ? processGroupAuthorizationSchema.parse(raw)
180
192
  : z.union([legacy.hvAuthorizationSchema, legacy.processGroupAuthorizationSchema]).parse(raw);
181
- const { namespace, group } = p.schemaVersion === 3 ? { namespace: p.namespace, group: p.groupId } : legacy.legacyPolicyIdentity(p);
193
+ const { namespace, group } = p.schemaVersion === 4 ? { namespace: p.namespace, group: p.generationId }
194
+ : p.schemaVersion === 3 ? { namespace: p.namespace, group: p.groupId } : legacy.legacyPolicyIdentity(p);
182
195
  if (p.stateRoot !== root ||
183
196
  file !== path.join(root, namespace, "authorizations", group + ".json") ||
184
197
  p.directory !== path.join(root, namespace, "admission", group))
@@ -194,14 +207,23 @@ export function configuredGroupProcessNames() {
194
207
  throw Error("Process-group authorization registry is missing, changed or insecure; review required");
195
208
  }
196
209
  }
197
- export function authorizeProcessGroupRuntime(context) {
210
+ function verifyRuntimePolicy(context, candidate) {
198
211
  try {
199
212
  const root = context.stateRoot ?? process.env["UNS_PROCESS_GROUP_STATE_ROOT"];
200
213
  if (!root || !context.controllerName)
201
214
  throw Error();
202
- const file = selectedFile(context.packageRoot, context.rawConfiguration, root, context.file), bytes = read(file, true), unparsed = JSON.parse(bytes.toString());
203
- if (unparsed.schemaVersion !== 3)
215
+ const file = selectedFile(context.packageRoot, context.rawConfiguration, root, context.file), bytes = candidate ?? read(file, true), unparsed = JSON.parse(bytes.toString());
216
+ if (unparsed.schemaVersion === 4) {
217
+ const result = verifyConfigurationGroupPolicy(context, bytes, root, file);
218
+ if (!candidate)
219
+ issued.add(result);
220
+ return result;
221
+ }
222
+ if (unparsed.schemaVersion !== 3) {
223
+ if (candidate)
224
+ throw Error("Only generic policies can be staged");
204
225
  return legacy.authorizeHvRuntime({ ...context, file, stateRoot: root });
226
+ }
205
227
  const p = processGroupAuthorizationSchema.parse(unparsed), d = processGroupDescriptor(context.packageRoot);
206
228
  if (!d || d.legacy)
207
229
  throw Error("Generic runtime descriptor required");
@@ -280,13 +302,28 @@ export function authorizeProcessGroupRuntime(context) {
280
302
  groupProcessName: m.processName,
281
303
  namespace: d.namespace,
282
304
  });
283
- issued.add(result);
305
+ if (!candidate)
306
+ issued.add(result);
284
307
  return result;
285
308
  }
286
309
  catch {
287
310
  throw Error("Process-group runtime authorization is missing, changed or does not match this installed placement; review required");
288
311
  }
289
312
  }
313
+ /** Live authority always comes from the current canonical private policy. */
314
+ export function authorizeProcessGroupRuntime(context) {
315
+ return verifyRuntimePolicy(context);
316
+ }
317
+ /** Review only: never issues an authorization accepted by runtime/launch checks. */
318
+ export function validateProcessGroupPolicyCandidate(context, bytes) {
319
+ verifyRuntimePolicy(context, bytes);
320
+ }
321
+ export function processGroupAuthorizationFile(context) {
322
+ const root = context.stateRoot ?? process.env["UNS_PROCESS_GROUP_STATE_ROOT"];
323
+ if (!root)
324
+ throw Error("Missing process-group state root");
325
+ return selectedFile(context.packageRoot, context.rawConfiguration, root, context.file);
326
+ }
290
327
  export const authorizeHvRuntime = authorizeProcessGroupRuntime;
291
328
  export function authorizeConfiguredProcessGroupRuntime(pkg, dir, raw, controllerName, initialize = false) {
292
329
  return authorizeProcessGroupRuntime({
@@ -313,7 +350,7 @@ export function assertProcessGroupStateMount(value, root) {
313
350
  return;
314
351
  }
315
352
  assertProcessGroupJournalAuthorization(value, value.directory, value.journalId ?? undefined);
316
- if (process.platform !== "linux" || !("namespace" in value) || value.directory !== path.join(root, value.namespace, "admission", value.groupId))
353
+ if (process.platform !== "linux" || !("namespace" in value) || value.directory !== path.join(root, value.namespace, "admission", value.generationId ?? value.groupId))
317
354
  throw Error("Process group requires its reviewed Linux state mount");
318
355
  const mount = fs
319
356
  .readFileSync("/proc/self/mountinfo", "utf8")
@@ -343,6 +380,9 @@ export function assertProcessGroupTargetLaunch(value, dir, operationId, launchId
343
380
  !z.string().uuid().safeParse(r.lastLaunch?.requestId).success ||
344
381
  !digest.safeParse(r.configurationChecksum).success)
345
382
  throw Error();
383
+ if ("configurationGroupSha256" in value && value.configurationGroupSha256 !== undefined &&
384
+ (r.configurationGroupSha256 !== value.configurationGroupSha256 || r.generationId !== value.generationId))
385
+ throw Error();
346
386
  }
347
387
  catch {
348
388
  throw Error("Target requires its exact reviewed group reservation and launch identity");