rampway 0.5.21 → 0.5.23

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 (34) hide show
  1. package/README.md +28 -6
  2. package/build/src/commands/runtime-boot.d.ts.map +1 -1
  3. package/build/src/commands/runtime-boot.js +34 -5
  4. package/build/src/commands/runtime-boot.js.map +1 -1
  5. package/build/src/core/deploy-runner.d.ts.map +1 -1
  6. package/build/src/core/deploy-runner.js +28 -5
  7. package/build/src/core/deploy-runner.js.map +1 -1
  8. package/build/src/core/runtime-boot-state.d.ts +8 -2
  9. package/build/src/core/runtime-boot-state.d.ts.map +1 -1
  10. package/build/src/core/runtime-boot-state.js +25 -5
  11. package/build/src/core/runtime-boot-state.js.map +1 -1
  12. package/build/src/core/runtime-errors.d.ts +23 -0
  13. package/build/src/core/runtime-errors.d.ts.map +1 -0
  14. package/build/src/core/runtime-errors.js +27 -0
  15. package/build/src/core/runtime-errors.js.map +1 -0
  16. package/build/src/core/runtime-owner-handoff.d.ts +2 -2
  17. package/build/src/core/runtime-owner-handoff.d.ts.map +1 -1
  18. package/build/src/core/runtime-owner-handoff.js +1 -1
  19. package/build/src/runtimes/rollbridge-runtime.d.ts +46 -4
  20. package/build/src/runtimes/rollbridge-runtime.d.ts.map +1 -1
  21. package/build/src/runtimes/rollbridge-runtime.js +314 -22
  22. package/build/src/runtimes/rollbridge-runtime.js.map +1 -1
  23. package/build/src/transports/local-transport.d.ts +19 -0
  24. package/build/src/transports/local-transport.d.ts.map +1 -1
  25. package/build/src/transports/local-transport.js +32 -0
  26. package/build/src/transports/local-transport.js.map +1 -1
  27. package/build/src/transports/ssh-transport.d.ts +38 -0
  28. package/build/src/transports/ssh-transport.d.ts.map +1 -1
  29. package/build/src/transports/ssh-transport.js +68 -0
  30. package/build/src/transports/ssh-transport.js.map +1 -1
  31. package/build/src/types.d.ts +28 -2
  32. package/build/src/types.d.ts.map +1 -1
  33. package/docs/config-reference.md +48 -7
  34. package/package.json +1 -1
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * @param {import('#types').Transport} transport
3
3
  * @param {import('#types').StageConfig} stage
4
- * @param {import('#types').RuntimeBootState} boot
4
+ * @param {import('#types').ReadRuntimeBootState} boot
5
5
  * @param {import('#types').LockInfo} lock
6
6
  * @returns {Promise<import('#types').RuntimeOwnerHandoff>}
7
7
  */
8
- export function writeRuntimeOwnerHandoff(transport: import("#types").Transport, stage: import("#types").StageConfig, boot: import("#types").RuntimeBootState, lock: import("#types").LockInfo): Promise<import("#types").RuntimeOwnerHandoff>;
8
+ export function writeRuntimeOwnerHandoff(transport: import("#types").Transport, stage: import("#types").StageConfig, boot: import("#types").ReadRuntimeBootState, lock: import("#types").LockInfo): Promise<import("#types").RuntimeOwnerHandoff>;
9
9
  /**
10
10
  * @param {import('#types').Transport} transport
11
11
  * @param {import('#types').StageConfig} stage
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-owner-handoff.d.ts","sourceRoot":"","sources":["../../../src/core/runtime-owner-handoff.js"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,oDANW,OAAO,QAAQ,EAAE,SAAS,SAC1B,OAAO,QAAQ,EAAE,WAAW,QAC5B,OAAO,QAAQ,EAAE,gBAAgB,QACjC,OAAO,QAAQ,EAAE,QAAQ,GACvB,OAAO,CAAC,OAAO,QAAQ,EAAE,mBAAmB,CAAC,CAUzD;AAED;;;;GAIG;AACH,mDAJW,OAAO,QAAQ,EAAE,SAAS,SAC1B,OAAO,QAAQ,EAAE,WAAW,GAC1B,OAAO,CAAC,OAAO,QAAQ,EAAE,mBAAmB,GAAC,IAAI,CAAC,CAc9D;AAED;;;;;GAKG;AACH,qDALW,OAAO,QAAQ,EAAE,SAAS,SAC1B,OAAO,QAAQ,EAAE,WAAW,WAC5B,OAAO,QAAQ,EAAE,mBAAmB,GAClC,OAAO,CAAC,IAAI,CAAC,CAKzB"}
1
+ {"version":3,"file":"runtime-owner-handoff.d.ts","sourceRoot":"","sources":["../../../src/core/runtime-owner-handoff.js"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,oDANW,OAAO,QAAQ,EAAE,SAAS,SAC1B,OAAO,QAAQ,EAAE,WAAW,QAC5B,OAAO,QAAQ,EAAE,oBAAoB,QACrC,OAAO,QAAQ,EAAE,QAAQ,GACvB,OAAO,CAAC,OAAO,QAAQ,EAAE,mBAAmB,CAAC,CAUzD;AAED;;;;GAIG;AACH,mDAJW,OAAO,QAAQ,EAAE,SAAS,SAC1B,OAAO,QAAQ,EAAE,WAAW,GAC1B,OAAO,CAAC,OAAO,QAAQ,EAAE,mBAAmB,GAAC,IAAI,CAAC,CAc9D;AAED;;;;;GAKG;AACH,qDALW,OAAO,QAAQ,EAAE,SAAS,SAC1B,OAAO,QAAQ,EAAE,WAAW,WAC5B,OAAO,QAAQ,EAAE,mBAAmB,GAClC,OAAO,CAAC,IAAI,CAAC,CAKzB"}
@@ -7,7 +7,7 @@ const DIGEST = /^sha256:[0-9a-f]{64}$/u;
7
7
  /**
8
8
  * @param {import('#types').Transport} transport
9
9
  * @param {import('#types').StageConfig} stage
10
- * @param {import('#types').RuntimeBootState} boot
10
+ * @param {import('#types').ReadRuntimeBootState} boot
11
11
  * @param {import('#types').LockInfo} lock
12
12
  * @returns {Promise<import('#types').RuntimeOwnerHandoff>}
13
13
  */
@@ -123,6 +123,20 @@ export class RollbridgeRuntime {
123
123
  externalOwnerVerificationRequired?: boolean;
124
124
  daemonRuntime?: any;
125
125
  }>;
126
+ /**
127
+ * Binds the exact immutable runtime reported by the accepted Rollbridge owner to
128
+ * Rampway's stable snapshot and stores an authenticated manifest copy beside the
129
+ * immutable Rollbridge config material.
130
+ *
131
+ * @param {import('#types').Transport} transport
132
+ * @param {{snapshot: import('#types').RuntimeSnapshot, runtimePath: string, runtimeResult: any}} opts
133
+ * @returns {Promise<import('#types').RuntimeSnapshot>}
134
+ */
135
+ acceptSnapshot(transport: import("#types").Transport, { snapshot, runtimePath, runtimeResult }: {
136
+ snapshot: import("#types").RuntimeSnapshot;
137
+ runtimePath: string;
138
+ runtimeResult: any;
139
+ }): Promise<import("#types").RuntimeSnapshot>;
126
140
  /**
127
141
  * Releases an exact stale foreground owner for a serial supervisor through
128
142
  * Rollbridge's authenticated owner-retirement contract, then waits for the
@@ -171,7 +185,7 @@ export class RollbridgeRuntime {
171
185
  acceptedAt: string;
172
186
  }): import("#types").RuntimeBootIdentity | undefined;
173
187
  /**
174
- * @param {{releasePath: string, releaseId: string, revision: string, configPath: string, bootAttestation?: string}} identity
188
+ * @param {{releasePath: string, releaseId: string, revision: string, configPath: string, bootAttestation?: string, snapshot?: import('#types').RuntimeSnapshot}} identity
175
189
  * @returns {string}
176
190
  */
177
191
  foregroundBootCommand(identity: {
@@ -180,17 +194,18 @@ export class RollbridgeRuntime {
180
194
  revision: string;
181
195
  configPath: string;
182
196
  bootAttestation?: string;
197
+ snapshot?: import("#types").RuntimeSnapshot;
183
198
  }): string;
184
199
  /**
185
200
  * @param {import('#types').Transport} transport
186
- * @param {import('#types').RuntimeBootState} state
201
+ * @param {import('#types').ReadRuntimeBootState} state
187
202
  * @returns {Promise<void>}
188
203
  */
189
- verifyForegroundBootMaterial(transport: import("#types").Transport, state: import("#types").RuntimeBootState): Promise<void>;
204
+ verifyForegroundBootMaterial(transport: import("#types").Transport, state: import("#types").ReadRuntimeBootState): Promise<void>;
190
205
  /**
191
206
  * Starts Rollbridge's foreground daemon contract without supervising it.
192
207
  *
193
- * @param {{releasePath: string, releaseId: string, revision: string, configPath: string, configIdentity: string, bootAttestation?: string, onStarted: () => Promise<void>}} identity
208
+ * @param {{releasePath: string, releaseId: string, revision: string, configPath: string, configIdentity: string, bootAttestation?: string, snapshot: import('#types').RuntimeSnapshot, onStarted: () => Promise<void>}} identity
194
209
  * @returns {Promise<{code: number}>}
195
210
  */
196
211
  foregroundBoot(identity: {
@@ -200,6 +215,7 @@ export class RollbridgeRuntime {
200
215
  configPath: string;
201
216
  configIdentity: string;
202
217
  bootAttestation?: string;
218
+ snapshot: import("#types").RuntimeSnapshot;
203
219
  onStarted: () => Promise<void>;
204
220
  }): Promise<{
205
221
  code: number;
@@ -234,6 +250,32 @@ export class RollbridgeRuntime {
234
250
  runtimePath: string;
235
251
  contract?: boolean;
236
252
  }): Promise<void>;
253
+ /**
254
+ * Hashes one accepted Rollbridge contract with its exact daemon runtime.
255
+ *
256
+ * @param {import('#types').RuntimeSnapshot} snapshot
257
+ * @param {{format: number, digest: string, path: string, version: string}} daemonRuntime
258
+ * @returns {string}
259
+ */
260
+ foregroundIdentityDigest(snapshot: import("#types").RuntimeSnapshot, daemonRuntime: {
261
+ format: number;
262
+ digest: string;
263
+ path: string;
264
+ version: string;
265
+ }): string;
266
+ /**
267
+ * Independently reproduces Rollbridge daemon-runtime format 1's content
268
+ * digest without executing any bytes from the accepted runtime.
269
+ *
270
+ * @param {import('#types').Transport} transport
271
+ * @param {{format: number, digest: string, path: string, version: string}} daemonRuntime
272
+ */
273
+ verifyReportedDaemonRuntime(transport: import("#types").Transport, daemonRuntime: {
274
+ format: number;
275
+ digest: string;
276
+ path: string;
277
+ version: string;
278
+ }): Promise<void>;
237
279
  /**
238
280
  * @param {import('#types').Transport} transport
239
281
  * @param {string} releasePath
@@ -1 +1 @@
1
- {"version":3,"file":"rollbridge-runtime.d.ts","sourceRoot":"","sources":["../../../src/runtimes/rollbridge-runtime.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rollbridge-runtime.d.ts","sourceRoot":"","sources":["../../../src/runtimes/rollbridge-runtime.js"],"names":[],"mappings":"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"}
@@ -2,7 +2,10 @@ import { createHash } from "node:crypto";
2
2
  import path from "node:path";
3
3
  import { shellQuote } from "../transports/ssh-transport.js";
4
4
  import { isSecretKey } from "../core/redaction.js";
5
+ import { RuntimeIncumbentPreservedError } from "../core/runtime-errors.js";
5
6
  import { spawnOwnedProcess, withOwnedProcessTree } from "../core/owned-process-tree.js";
7
+ const ROLLBRIDGE_ATOMIC_OWNER_REPLACEMENT_MINIMUM = "0.1.29";
8
+ const ROLLBRIDGE_VERIFIED_DAEMON_RUNTIME_MINIMUM = "0.1.38";
6
9
  export class RollbridgeRuntime {
7
10
  /** @type {"rollbridge"} */
8
11
  static type = "rollbridge";
@@ -162,8 +165,9 @@ export class RollbridgeRuntime {
162
165
  const configContent = sourceConfigPath ? await transport.readFile(sourceConfigPath) : undefined;
163
166
  const configName = sourceConfigPath ? path.basename(sourceConfigPath) : undefined;
164
167
  const packageVersion = await this.packageVersion(transport, releasePath, this.config);
165
- if (this.config.externalOwner && !supportsExternalOwner(packageVersion))
166
- throw new Error("Rollbridge external foreground ownership requires Rollbridge 0.1.21 or newer in the candidate release.");
168
+ if (this.config.externalOwner && !supportsVerifiedDaemonRuntime(packageVersion)) {
169
+ throw new Error(`Rollbridge external foreground ownership requires Rollbridge ${ROLLBRIDGE_VERIFIED_DAEMON_RUNTIME_MINIMUM} or newer with an exact content-addressed daemon runtime in the candidate release.`);
170
+ }
167
171
  const packageIdentity = packageVersion ? `version:${packageVersion}` : `release:${releasePath}`;
168
172
  const digest = this.contractDigest({ config: this.config, configContent, configName, packageIdentity });
169
173
  const contractDigest = `sha256:${digest}`;
@@ -194,6 +198,7 @@ export class RollbridgeRuntime {
194
198
  schemaVersion: 1,
195
199
  type: "rollbridge",
196
200
  contractDigest,
201
+ command: this.config.command,
197
202
  materialDigest: materialDigest ? `sha256:${materialDigest}` : undefined,
198
203
  packageDir: this.config.packageDir,
199
204
  configPath,
@@ -217,6 +222,9 @@ export class RollbridgeRuntime {
217
222
  * @returns {Promise<{status: string, externalOwnerVerificationRequired?: boolean, daemonRuntime?: any}>}
218
223
  */
219
224
  async deploy(transport, { releasePath, releaseId, revision, currentPath, targetSnapshot, currentSnapshot, deferCommit = false }) {
225
+ if (this.config.externalOwner && targetSnapshot && !supportsVerifiedDaemonRuntime(targetSnapshot.packageVersion)) {
226
+ throw new Error(`Rollbridge external foreground ownership requires Rollbridge ${ROLLBRIDGE_VERIFIED_DAEMON_RUNTIME_MINIMUM} or newer with an exact content-addressed daemon runtime before deployment.`);
227
+ }
220
228
  if (this.config.externalOwner && targetSnapshot && currentPath && currentSnapshot &&
221
229
  currentSnapshot.contractDigest !== targetSnapshot.contractDigest && !supportsExternalOwner(currentSnapshot.packageVersion)) {
222
230
  throw unsupportedExternalOwnerUpgrade(currentSnapshot.packageVersion);
@@ -229,10 +237,12 @@ export class RollbridgeRuntime {
229
237
  throw new Error(`Rollbridge validation failed: ${ /** @type {Error} */(error).message}`);
230
238
  }
231
239
  let releaseVersion;
240
+ const atomicOwnerReplacement = supportsAtomicOwnerReplacement(targetSnapshot?.packageVersion);
241
+ const atomicIncompatibleReplacement = atomicOwnerReplacement && Boolean(currentPath && currentSnapshot?.contractDigest !== targetSnapshot?.contractDigest);
232
242
  const externalOwnerVerificationRequired = Boolean(this.config.externalOwner);
233
243
  if (targetSnapshot) {
234
244
  releaseVersion = targetSnapshot.packageVersion;
235
- if (currentPath && currentSnapshot?.contractDigest !== targetSnapshot.contractDigest) {
245
+ if (!atomicOwnerReplacement && currentPath && currentSnapshot?.contractDigest !== targetSnapshot.contractDigest) {
236
246
  await this.shutdownChangedVersion(transport, { currentPath, snapshot: currentSnapshot ?? targetSnapshot });
237
247
  }
238
248
  }
@@ -244,7 +254,10 @@ export class RollbridgeRuntime {
244
254
  await transport.run(deployCommand, this.runOptions(releasePath, targetConfig));
245
255
  }
246
256
  catch (error) {
247
- if (!currentPath || !currentSnapshot || !isLegacyRuntimeMismatch(/** @type {Error} */ (error))) {
257
+ if (atomicIncompatibleReplacement) {
258
+ throw new RuntimeIncumbentPreservedError(`Rollbridge deploy failed: ${ /** @type {Error} */(error).message}`, { cause: error });
259
+ }
260
+ if (atomicOwnerReplacement || !currentPath || !currentSnapshot || !isLegacyRuntimeMismatch(/** @type {Error} */ (error))) {
248
261
  throw new Error(`Rollbridge deploy failed: ${ /** @type {Error} */(error).message}`);
249
262
  }
250
263
  try {
@@ -267,6 +280,62 @@ export class RollbridgeRuntime {
267
280
  const status = externalOwnerVerificationRequired ? parseStatus(acceptedStatus.output) : {};
268
281
  return { status: "deployed", ...(externalOwnerVerificationRequired ? { externalOwnerVerificationRequired: true, daemonRuntime: status.daemonRuntime } : {}) };
269
282
  }
283
+ /**
284
+ * Binds the exact immutable runtime reported by the accepted Rollbridge owner to
285
+ * Rampway's stable snapshot and stores an authenticated manifest copy beside the
286
+ * immutable Rollbridge config material.
287
+ *
288
+ * @param {import('#types').Transport} transport
289
+ * @param {{snapshot: import('#types').RuntimeSnapshot, runtimePath: string, runtimeResult: any}} opts
290
+ * @returns {Promise<import('#types').RuntimeSnapshot>}
291
+ */
292
+ async acceptSnapshot(transport, { snapshot, runtimePath, runtimeResult }) {
293
+ if (!this.config.externalOwner)
294
+ return snapshot;
295
+ const daemonRuntime = parseDaemonRuntime(runtimeResult?.daemonRuntime);
296
+ if (daemonRuntime.version !== snapshot.packageVersion)
297
+ throw new Error("Rollbridge daemon runtime version does not match the accepted package identity.");
298
+ if (snapshot.schemaVersion === 2) {
299
+ const acceptedRuntime = parseDaemonRuntime(snapshot.daemonRuntime);
300
+ if (canonicalJson(daemonRuntime) !== canonicalJson(acceptedRuntime)) {
301
+ throw new Error("Rollbridge activated a daemon runtime that does not match the retained accepted runtime identity.");
302
+ }
303
+ await this.verifySnapshot(transport, { snapshot, runtimePath });
304
+ return snapshot;
305
+ }
306
+ const manifestRoot = path.join(runtimePath, "rollbridge-daemon", daemonRuntime.digest);
307
+ const manifestPath = path.join(manifestRoot, "runtime.json");
308
+ const manifestContent = `${JSON.stringify(daemonRuntime, null, 2)}\n`;
309
+ await this.verifyReportedDaemonRuntime(transport, daemonRuntime);
310
+ if (await transport.exists(manifestPath)) {
311
+ if (await transport.readFile(manifestPath) !== manifestContent)
312
+ throw new Error(`Stored Rollbridge daemon runtime manifest does not match its accepted identity: ${manifestPath}`);
313
+ }
314
+ else {
315
+ if (await transport.exists(manifestRoot))
316
+ throw new Error(`Stored Rollbridge daemon runtime manifest is incomplete: ${manifestRoot}`);
317
+ const pendingRoot = path.join(runtimePath, `.rollbridge-daemon-${daemonRuntime.digest}-${process.pid}-${Date.now()}`);
318
+ await transport.mkdirp(pendingRoot);
319
+ try {
320
+ await transport.writeFile(path.join(pendingRoot, "runtime.json"), manifestContent);
321
+ await transport.mkdirp(path.dirname(manifestRoot));
322
+ await transport.rename(pendingRoot, manifestRoot);
323
+ }
324
+ finally {
325
+ if (await transport.exists(pendingRoot))
326
+ await transport.rm(pendingRoot);
327
+ }
328
+ }
329
+ const accepted = {
330
+ ...snapshot,
331
+ schemaVersion: 2,
332
+ daemonRuntime,
333
+ daemonRuntimeManifestPath: manifestPath,
334
+ foregroundIdentityDigest: this.foregroundIdentityDigest(snapshot, daemonRuntime)
335
+ };
336
+ await this.verifySnapshot(transport, { snapshot: accepted, runtimePath });
337
+ return accepted;
338
+ }
270
339
  /**
271
340
  * Releases an exact stale foreground owner for a serial supervisor through
272
341
  * Rollbridge's authenticated owner-retirement contract, then waits for the
@@ -341,21 +410,25 @@ export class RollbridgeRuntime {
341
410
  if (!snapshot.configPath || !snapshot.materialDigest || !supportsForegroundBoot(snapshot.packageVersion))
342
411
  return undefined;
343
412
  this.validateSnapshot(snapshot);
344
- return { runtimeType: "rollbridge", releaseId, revision, releasePath: path.resolve(releasePath), configPath: path.resolve(/** @type {string} */ (snapshot.configPath)), configIdentity: /** @type {string} */ (snapshot.materialDigest), acceptedAt };
413
+ const snapshotIdentity = snapshot.schemaVersion === 2 ? snapshot.foregroundIdentityDigest : snapshot.contractDigest;
414
+ return { runtimeType: "rollbridge", releaseId, revision, releasePath: path.resolve(releasePath), configPath: path.resolve(/** @type {string} */ (snapshot.configPath)), configIdentity: /** @type {string} */ (snapshot.materialDigest), snapshotIdentity: /** @type {string} */ (snapshotIdentity), acceptedAt };
345
415
  }
346
416
  /**
347
- * @param {{releasePath: string, releaseId: string, revision: string, configPath: string, bootAttestation?: string}} identity
417
+ * @param {{releasePath: string, releaseId: string, revision: string, configPath: string, bootAttestation?: string, snapshot?: import('#types').RuntimeSnapshot}} identity
348
418
  * @returns {string}
349
419
  */
350
420
  foregroundBootCommand(identity) {
351
421
  const args = ["daemon", "--config", identity.configPath, "--release-path", identity.releasePath, "--release-id", identity.releaseId, "--revision", identity.revision];
352
422
  if (this.config.externalOwner && identity.bootAttestation)
353
423
  args.push("--boot-attestation", identity.bootAttestation);
354
- return `${this.config.command} ${args.map(value => shellQuote(value)).join(" ")}`;
424
+ const command = identity.snapshot?.schemaVersion === 2 && identity.snapshot.daemonRuntime
425
+ ? shellQuote(path.join(identity.snapshot.daemonRuntime.path, "bin", "rollbridge"))
426
+ : this.config.command;
427
+ return `${command} ${args.map(value => shellQuote(value)).join(" ")}`;
355
428
  }
356
429
  /**
357
430
  * @param {import('#types').Transport} transport
358
- * @param {import('#types').RuntimeBootState} state
431
+ * @param {import('#types').ReadRuntimeBootState} state
359
432
  * @returns {Promise<void>}
360
433
  */
361
434
  async verifyForegroundBootMaterial(transport, state) {
@@ -367,13 +440,21 @@ export class RollbridgeRuntime {
367
440
  /**
368
441
  * Starts Rollbridge's foreground daemon contract without supervising it.
369
442
  *
370
- * @param {{releasePath: string, releaseId: string, revision: string, configPath: string, configIdentity: string, bootAttestation?: string, onStarted: () => Promise<void>}} identity
443
+ * @param {{releasePath: string, releaseId: string, revision: string, configPath: string, configIdentity: string, bootAttestation?: string, snapshot: import('#types').RuntimeSnapshot, onStarted: () => Promise<void>}} identity
371
444
  * @returns {Promise<{code: number}>}
372
445
  */
373
446
  async foregroundBoot(identity) {
374
447
  return await withOwnedProcessTree(async () => {
375
448
  const command = this.foregroundBootCommand(identity);
376
- const child = spawnOwnedProcess(`exec ${command}`, { cwd: identity.releasePath, env: { ...process.env, ...this.config.env }, shell: true, stdio: "inherit" });
449
+ const runtimeManifest = identity.snapshot?.schemaVersion === 2 && identity.snapshot.daemonRuntime
450
+ ? path.join(identity.snapshot.daemonRuntime.path, "runtime.json")
451
+ : undefined;
452
+ const child = spawnOwnedProcess(`exec ${command}`, {
453
+ cwd: identity.releasePath,
454
+ env: { ...process.env, ...this.config.env, ...(runtimeManifest ? { ROLLBRIDGE_DAEMON_RUNTIME_MANIFEST: runtimeManifest } : {}) },
455
+ shell: true,
456
+ stdio: "inherit"
457
+ });
377
458
  const signals = /** @type {const} */ (["SIGINT", "SIGTERM", "SIGHUP"]);
378
459
  const handlers = new Map(signals.map(signal => [signal, () => child.kill(signal)]));
379
460
  for (const [signal, handler] of handlers)
@@ -430,6 +511,26 @@ export class RollbridgeRuntime {
430
511
  if (!isReleaseRelativePath(snapshot.packageDir)) {
431
512
  throw new Error("Stored Rollbridge runtime state has an unsafe package directory.");
432
513
  }
514
+ if (snapshot.command !== undefined && (typeof snapshot.command !== "string" || !snapshot.command.trim() || snapshot.command.includes("\0"))) {
515
+ throw new Error("Stored Rollbridge runtime state has an invalid accepted command.");
516
+ }
517
+ if (snapshot.schemaVersion === 2) {
518
+ const daemonRuntime = parseDaemonRuntime(snapshot.daemonRuntime);
519
+ if (daemonRuntime.version !== snapshot.packageVersion)
520
+ throw new Error("Stored Rollbridge daemon runtime version does not match its accepted package identity.");
521
+ if (typeof snapshot.daemonRuntimeManifestPath !== "string" || !path.isAbsolute(snapshot.daemonRuntimeManifestPath) || path.normalize(snapshot.daemonRuntimeManifestPath) !== snapshot.daemonRuntimeManifestPath) {
522
+ throw new Error("Stored Rollbridge runtime state has an invalid daemon runtime manifest path.");
523
+ }
524
+ if (!/^sha256:[0-9a-f]{64}$/u.test(snapshot.foregroundIdentityDigest ?? "")) {
525
+ throw new Error("Stored Rollbridge runtime state has an invalid foreground runtime identity digest.");
526
+ }
527
+ if (snapshot.foregroundIdentityDigest !== this.foregroundIdentityDigest(snapshot, /** @type {any} */ (snapshot.daemonRuntime))) {
528
+ throw new Error("Stored Rollbridge foreground runtime identity no longer matches its known-good digest.");
529
+ }
530
+ }
531
+ else if (snapshot.schemaVersion !== undefined && snapshot.schemaVersion !== 1) {
532
+ throw new Error("Stored Rollbridge runtime state has an unsupported schema version.");
533
+ }
433
534
  if (snapshot.rollbackCompatibility !== undefined && (typeof snapshot.rollbackCompatibility !== "string" || snapshot.rollbackCompatibility.trim().length === 0 || snapshot.rollbackCompatibility.includes("\0"))) {
434
535
  throw new Error("Stored Rollbridge runtime state has an invalid rollback compatibility identifier.");
435
536
  }
@@ -447,8 +548,9 @@ export class RollbridgeRuntime {
447
548
  * @returns {void}
448
549
  */
449
550
  validateRollbackTarget(snapshot) {
450
- if (this.config.externalOwner && !supportsExternalOwner(snapshot.packageVersion))
451
- throw new Error("Rollbridge external foreground ownership requires Rollbridge 0.1.21 or newer in the retained rollback release.");
551
+ if (this.config.externalOwner && !supportsVerifiedDaemonRuntime(snapshot.packageVersion)) {
552
+ throw new Error(`Rollbridge external foreground ownership requires Rollbridge ${ROLLBRIDGE_VERIFIED_DAEMON_RUNTIME_MINIMUM} or newer with an exact content-addressed daemon runtime in the retained rollback release.`);
553
+ }
452
554
  if (this.config.externalOwner && (typeof snapshot.socketPath !== "string" || snapshot.socketPath.trim().length === 0 || snapshot.socketPath.includes("\0")))
453
555
  throw new Error("Rollbridge external foreground ownership requires a valid control socket path in the retained rollback release; redeploy the target release once with the current Rampway so its runtime snapshot records runtime.socketPath before rolling back to it.");
454
556
  if (snapshot.rollbackCompatibility !== this.config.rollbackCompatibility) {
@@ -505,6 +607,24 @@ export class RollbridgeRuntime {
505
607
  if (this.materialDigest(path.basename(configPath), configContent) !== materialDigest) {
506
608
  throw new Error("Stored Rollbridge config material no longer matches its content digest.");
507
609
  }
610
+ if (snapshot.schemaVersion === 2) {
611
+ const manifestPath = /** @type {string} */ (snapshot.daemonRuntimeManifestPath);
612
+ const daemonRuntime = parseDaemonRuntime(snapshot.daemonRuntime);
613
+ const trustedDaemonRoot = path.join(runtimePath, "rollbridge-daemon");
614
+ if (path.resolve(manifestPath) !== path.resolve(trustedDaemonRoot, daemonRuntime.digest, "runtime.json")) {
615
+ throw new Error("Stored Rollbridge daemon runtime manifest path is outside its known-good runtime directory.");
616
+ }
617
+ const trustedDaemonRealPath = await transport.realpath(trustedDaemonRoot);
618
+ const trustedIdentityRealPath = await transport.realpath(path.dirname(manifestPath));
619
+ const manifestRealPath = await transport.realpath(manifestPath);
620
+ if (path.relative(trustedDaemonRealPath, trustedIdentityRealPath) !== daemonRuntime.digest || path.dirname(manifestRealPath) !== trustedIdentityRealPath) {
621
+ throw new Error("Stored Rollbridge daemon runtime manifest resolves outside its known-good runtime directory.");
622
+ }
623
+ if (await transport.readFile(manifestPath) !== `${JSON.stringify(daemonRuntime, null, 2)}\n`) {
624
+ throw new Error("Stored Rollbridge daemon runtime manifest no longer matches its accepted identity.");
625
+ }
626
+ await this.verifyReportedDaemonRuntime(transport, daemonRuntime);
627
+ }
508
628
  if (!contract)
509
629
  return;
510
630
  const config = this.configForSnapshot(snapshot);
@@ -517,6 +637,54 @@ export class RollbridgeRuntime {
517
637
  if (actualDigest !== digest)
518
638
  throw new Error("Stored Rollbridge runtime contract no longer matches its known-good digest.");
519
639
  }
640
+ /**
641
+ * Hashes one accepted Rollbridge contract with its exact daemon runtime.
642
+ *
643
+ * @param {import('#types').RuntimeSnapshot} snapshot
644
+ * @param {{format: number, digest: string, path: string, version: string}} daemonRuntime
645
+ * @returns {string}
646
+ */
647
+ foregroundIdentityDigest(snapshot, daemonRuntime) {
648
+ return `sha256:${createHash("sha256").update(canonicalJson({ contractDigest: snapshot.contractDigest, daemonRuntime })).digest("hex")}`;
649
+ }
650
+ /**
651
+ * Independently reproduces Rollbridge daemon-runtime format 1's content
652
+ * digest without executing any bytes from the accepted runtime.
653
+ *
654
+ * @param {import('#types').Transport} transport
655
+ * @param {{format: number, digest: string, path: string, version: string}} daemonRuntime
656
+ */
657
+ async verifyReportedDaemonRuntime(transport, daemonRuntime) {
658
+ const runtimeRealPath = await transport.realpath(daemonRuntime.path);
659
+ if (runtimeRealPath !== daemonRuntime.path)
660
+ throw new Error("Accepted Rollbridge daemon runtime resolves through a mutable or redirected path.");
661
+ const manifestPath = path.join(daemonRuntime.path, "runtime.json");
662
+ const binaryPath = path.join(daemonRuntime.path, "bin", "rollbridge");
663
+ if (!await transport.exists(manifestPath) || await transport.isDirectory(manifestPath) || !await transport.exists(binaryPath) || await transport.isDirectory(binaryPath)) {
664
+ throw new Error("Accepted Rollbridge daemon runtime material is incomplete.");
665
+ }
666
+ let manifest;
667
+ try {
668
+ manifest = JSON.parse(await transport.readFile(manifestPath));
669
+ }
670
+ catch (error) {
671
+ throw new Error(`Accepted Rollbridge daemon runtime manifest is malformed: ${ /** @type {Error} */(error).message}`);
672
+ }
673
+ if (canonicalJson(manifest) !== canonicalJson(daemonRuntime))
674
+ throw new Error("Accepted Rollbridge daemon runtime manifest does not match the reported identity.");
675
+ let packageMetadata;
676
+ try {
677
+ packageMetadata = JSON.parse(await transport.readFile(path.join(daemonRuntime.path, "package.json")));
678
+ }
679
+ catch (error) {
680
+ throw new Error(`Accepted Rollbridge daemon runtime package metadata is malformed: ${ /** @type {Error} */(error).message}`);
681
+ }
682
+ if (packageMetadata?.version !== daemonRuntime.version)
683
+ throw new Error("Accepted Rollbridge daemon runtime package version does not match the reported identity.");
684
+ const actualDigest = await rollbridgeDaemonRuntimeDigest(transport, daemonRuntime.path);
685
+ if (actualDigest !== daemonRuntime.digest)
686
+ throw new Error("Accepted Rollbridge daemon runtime bytes no longer match their content digest.");
687
+ }
520
688
  /**
521
689
  * @param {import('#types').Transport} transport
522
690
  * @param {string} releasePath
@@ -732,7 +900,7 @@ export class RollbridgeRuntime {
732
900
  }
733
901
  return {
734
902
  type: "rollbridge",
735
- command: this.config.command,
903
+ command: snapshot.command ?? this.config.command,
736
904
  packageDir: snapshot.packageDir,
737
905
  config: snapshot.configPath,
738
906
  daemonLogPath: snapshot.daemonLogPath,
@@ -817,22 +985,65 @@ function isLegacyRuntimeMismatch(error) {
817
985
  * @returns {boolean}
818
986
  */
819
987
  function supportsForegroundBoot(version) {
820
- const match = /^(\d+)\.(\d+)\.(\d+)$/u.exec(version ?? "");
821
- if (!match)
822
- return false;
823
- const [major, minor, patch] = match.slice(1).map(Number);
824
- return major > 0 || minor > 1 || (minor === 1 && patch >= 17);
988
+ return rollbridgeVersionAtLeast(version, "0.1.17");
825
989
  }
826
990
  /**
827
991
  * @param {string|undefined} version
828
992
  * @returns {boolean}
829
993
  */
830
994
  function supportsExternalOwner(version) {
831
- const match = /^(\d+)\.(\d+)\.(\d+)$/u.exec(version ?? "");
832
- if (!match)
995
+ return rollbridgeVersionAtLeast(version, "0.1.21");
996
+ }
997
+ /**
998
+ * Exact foreground recovery requires the Rollbridge contract that reports and
999
+ * restores the guardian's committed content-addressed runtime authority.
1000
+ * Earlier external owners can legitimately report a release package path.
1001
+ *
1002
+ * @param {string|undefined} version
1003
+ * @returns {boolean}
1004
+ */
1005
+ function supportsVerifiedDaemonRuntime(version) {
1006
+ return rollbridgeVersionAtLeast(version, ROLLBRIDGE_VERIFIED_DAEMON_RUNTIME_MINIMUM);
1007
+ }
1008
+ /**
1009
+ * Rollbridge 0.1.29 introduced authenticated, atomic replacement for an
1010
+ * incompatible config, socket, package, or runtime owner. Capability comes
1011
+ * only from the prepared candidate snapshot; the incumbent daemon cannot
1012
+ * safely assert what replacement transaction the candidate implements.
1013
+ *
1014
+ * @param {string|undefined} version
1015
+ * @returns {boolean}
1016
+ */
1017
+ function supportsAtomicOwnerReplacement(version) {
1018
+ return rollbridgeVersionAtLeast(version, ROLLBRIDGE_ATOMIC_OWNER_REPLACEMENT_MINIMUM);
1019
+ }
1020
+ /**
1021
+ * Compares strict release versions using the adapter's established
1022
+ * three-component Rollbridge version format. Prereleases and malformed values
1023
+ * fail closed instead of being granted a capability.
1024
+ *
1025
+ * @param {string|undefined} version
1026
+ * @param {string} minimum
1027
+ * @returns {boolean}
1028
+ */
1029
+ function rollbridgeVersionAtLeast(version, minimum) {
1030
+ const candidate = parseRollbridgeVersion(version);
1031
+ const boundary = parseRollbridgeVersion(minimum);
1032
+ if (!candidate || !boundary)
833
1033
  return false;
834
- const [major, minor, patch] = match.slice(1).map(Number);
835
- return major > 0 || minor > 1 || (minor === 1 && patch >= 21);
1034
+ for (let index = 0; index < candidate.length; index += 1) {
1035
+ if (candidate[index] !== boundary[index])
1036
+ return candidate[index] > boundary[index];
1037
+ }
1038
+ return true;
1039
+ }
1040
+ /**
1041
+ * @param {string|undefined} version
1042
+ * @returns {[number, number, number]|undefined}
1043
+ */
1044
+ function parseRollbridgeVersion(version) {
1045
+ const match = /^(\d+)\.(\d+)\.(\d+)$/u.exec(version ?? "");
1046
+ return match ? /** @type {[number, number, number]} */ (match.slice(1).map(Number)) : undefined;
836
1047
  }
837
1048
  /**
838
1049
  * @param {string|undefined} output
@@ -934,6 +1145,87 @@ function canonicalJson(value) {
934
1145
  }
935
1146
  return JSON.stringify(value);
936
1147
  }
1148
+ /**
1149
+ * Reproduces Rollbridge daemon-runtime format 1: sorted root-relative file
1150
+ * paths and raw bytes, each separated by NUL, with runtime.json excluded.
1151
+ * Symbolic links and every non-file/non-directory entry fail closed exactly as
1152
+ * they do while Rollbridge materializes its immutable runtime.
1153
+ *
1154
+ * @param {import('#types').Transport} transport
1155
+ * @param {string} rootPath
1156
+ * @returns {Promise<string>}
1157
+ */
1158
+ async function rollbridgeDaemonRuntimeDigest(transport, rootPath) {
1159
+ if (typeof transport.stat !== "function" || typeof transport.readFileBuffer !== "function" || typeof transport.listDirectory !== "function") {
1160
+ throw new Error("The selected transport cannot independently verify Rollbridge daemon runtime bytes.");
1161
+ }
1162
+ const stat = transport.stat;
1163
+ const readFileBuffer = transport.readFileBuffer;
1164
+ const listDirectory = transport.listDirectory;
1165
+ /** @type {string[]} */
1166
+ const files = [];
1167
+ /** @param {string} relativePath */
1168
+ const walk = async (relativePath) => {
1169
+ const directoryPath = path.join(rootPath, relativePath);
1170
+ const directory = await stat(directoryPath);
1171
+ if (!directory || directory.isSymbolicLink?.() || !directory.isDirectory?.()) {
1172
+ throw new Error(`Unsupported entry in accepted Rollbridge daemon runtime: ${directoryPath}`);
1173
+ }
1174
+ const entries = await listDirectory(directoryPath);
1175
+ const uniqueEntries = new Set(entries);
1176
+ if (uniqueEntries.size !== entries.length)
1177
+ throw new Error(`Duplicate entry in accepted Rollbridge daemon runtime: ${directoryPath}`);
1178
+ for (const entry of entries.sort((left, right) => left.localeCompare(right))) {
1179
+ if (!entry || entry === "." || entry === ".." || entry.includes("\0") || path.basename(entry) !== entry) {
1180
+ throw new Error(`Invalid entry in accepted Rollbridge daemon runtime: ${entry}`);
1181
+ }
1182
+ const childRelativePath = path.join(relativePath, entry);
1183
+ const childPath = path.join(rootPath, childRelativePath);
1184
+ const child = await stat(childPath);
1185
+ if (!child || child.isSymbolicLink?.())
1186
+ throw new Error(`Unsupported entry in accepted Rollbridge daemon runtime: ${childPath}`);
1187
+ if (child.isDirectory?.())
1188
+ await walk(childRelativePath);
1189
+ else if (child.isFile?.())
1190
+ files.push(childRelativePath);
1191
+ else
1192
+ throw new Error(`Unsupported entry in accepted Rollbridge daemon runtime: ${childPath}`);
1193
+ }
1194
+ };
1195
+ await walk("");
1196
+ const hash = createHash("sha256");
1197
+ for (const relativePath of files) {
1198
+ if (relativePath === "runtime.json")
1199
+ continue;
1200
+ const content = await readFileBuffer(path.join(rootPath, relativePath));
1201
+ if (!Buffer.isBuffer(content))
1202
+ throw new Error(`Transport returned non-byte content for accepted Rollbridge daemon runtime: ${relativePath}`);
1203
+ hash.update(relativePath);
1204
+ hash.update("\0");
1205
+ hash.update(content);
1206
+ hash.update("\0");
1207
+ }
1208
+ return hash.digest("hex");
1209
+ }
1210
+ /**
1211
+ * @param {unknown} value
1212
+ * @returns {{format: number, digest: string, path: string, version: string}}
1213
+ */
1214
+ function parseDaemonRuntime(value) {
1215
+ if (!value || typeof value !== "object" || Array.isArray(value))
1216
+ throw new Error("Accepted Rollbridge owner did not report a valid immutable daemon runtime identity.");
1217
+ const runtime = /** @type {Record<string, unknown>} */ (value);
1218
+ const keys = Object.keys(runtime).sort();
1219
+ if (JSON.stringify(keys) !== JSON.stringify(["digest", "format", "path", "version"]))
1220
+ throw new Error("Accepted Rollbridge daemon runtime identity contains unsupported material.");
1221
+ if (runtime.format !== 1 || typeof runtime.digest !== "string" || !/^[0-9a-f]{64}$/u.test(runtime.digest) || typeof runtime.version !== "string" || !supportsForegroundBoot(runtime.version)) {
1222
+ throw new Error("Accepted Rollbridge owner reported a malformed immutable daemon runtime identity.");
1223
+ }
1224
+ if (typeof runtime.path !== "string" || !path.isAbsolute(runtime.path) || path.normalize(runtime.path) !== runtime.path || path.basename(runtime.path) !== runtime.digest) {
1225
+ throw new Error("Accepted Rollbridge daemon runtime path is not its normalized content-addressed identity.");
1226
+ }
1227
+ return { format: runtime.format, digest: runtime.digest, path: runtime.path, version: runtime.version };
1228
+ }
937
1229
  /**
938
1230
  * @param {unknown} configuredPath
939
1231
  * @returns {configuredPath is string}