rightsize 0.7.8 → 0.7.10

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 (75) hide show
  1. package/dist/backend-docker/backend.d.ts +4 -1
  2. package/dist/backend-docker/backend.d.ts.map +1 -1
  3. package/dist/backend-docker/backend.js +4 -0
  4. package/dist/backend-docker/backend.js.map +1 -1
  5. package/dist/backend-msb/backend.d.ts +618 -68
  6. package/dist/backend-msb/backend.d.ts.map +1 -1
  7. package/dist/backend-msb/backend.js +1236 -133
  8. package/dist/backend-msb/backend.js.map +1 -1
  9. package/dist/backend-msb/commands.d.ts +160 -16
  10. package/dist/backend-msb/commands.d.ts.map +1 -1
  11. package/dist/backend-msb/commands.js +197 -31
  12. package/dist/backend-msb/commands.js.map +1 -1
  13. package/dist/backend-msb/provisioner.d.ts +1 -1
  14. package/dist/backend-msb/provisioner.d.ts.map +1 -1
  15. package/dist/backend-msb/provisioner.js +1 -1
  16. package/dist/backend-msb/provisioner.js.map +1 -1
  17. package/dist/backend-msb/restore-access-denied.d.ts +50 -0
  18. package/dist/backend-msb/restore-access-denied.d.ts.map +1 -0
  19. package/dist/backend-msb/restore-access-denied.js +52 -0
  20. package/dist/backend-msb/restore-access-denied.js.map +1 -0
  21. package/dist/backend-msb/restore-broker.d.ts +181 -0
  22. package/dist/backend-msb/restore-broker.d.ts.map +1 -0
  23. package/dist/backend-msb/restore-broker.js +264 -0
  24. package/dist/backend-msb/restore-broker.js.map +1 -0
  25. package/dist/backend-msb/sandbox-already-exists.d.ts +32 -0
  26. package/dist/backend-msb/sandbox-already-exists.d.ts.map +1 -0
  27. package/dist/backend-msb/sandbox-already-exists.js +35 -0
  28. package/dist/backend-msb/sandbox-already-exists.js.map +1 -0
  29. package/dist/backend-msb/snapshot-create.d.ts +22 -0
  30. package/dist/backend-msb/snapshot-create.d.ts.map +1 -0
  31. package/dist/backend-msb/snapshot-create.js +33 -0
  32. package/dist/backend-msb/snapshot-create.js.map +1 -0
  33. package/dist/backend-msb/snapshot-import.d.ts +30 -14
  34. package/dist/backend-msb/snapshot-import.d.ts.map +1 -1
  35. package/dist/backend-msb/snapshot-import.js +35 -17
  36. package/dist/backend-msb/snapshot-import.js.map +1 -1
  37. package/dist/backend-msb/snapshot-rm.d.ts +28 -0
  38. package/dist/backend-msb/snapshot-rm.d.ts.map +1 -0
  39. package/dist/backend-msb/snapshot-rm.js +30 -0
  40. package/dist/backend-msb/snapshot-rm.js.map +1 -0
  41. package/dist/core/backend.d.ts +53 -22
  42. package/dist/core/backend.d.ts.map +1 -1
  43. package/dist/core/checkpoint/api.d.ts +2 -2
  44. package/dist/core/checkpoint/api.js +2 -2
  45. package/dist/core/checkpoint/registry.d.ts +34 -0
  46. package/dist/core/checkpoint/registry.d.ts.map +1 -1
  47. package/dist/core/checkpoint/registry.js +20 -1
  48. package/dist/core/checkpoint/registry.js.map +1 -1
  49. package/dist/core/errors.d.ts +49 -0
  50. package/dist/core/errors.d.ts.map +1 -1
  51. package/dist/core/errors.js +61 -0
  52. package/dist/core/errors.js.map +1 -1
  53. package/dist/core/generic-container.d.ts +58 -10
  54. package/dist/core/generic-container.d.ts.map +1 -1
  55. package/dist/core/generic-container.js +222 -27
  56. package/dist/core/generic-container.js.map +1 -1
  57. package/dist/core/model.d.ts +3 -2
  58. package/dist/core/model.d.ts.map +1 -1
  59. package/dist/core/sandbox-name.d.ts +3 -0
  60. package/dist/core/sandbox-name.d.ts.map +1 -0
  61. package/dist/core/sandbox-name.js +19 -0
  62. package/dist/core/sandbox-name.js.map +1 -0
  63. package/dist/index.d.ts +1 -1
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +1 -1
  66. package/dist/index.js.map +1 -1
  67. package/dist/modules/minio.d.ts +5 -3
  68. package/dist/modules/minio.d.ts.map +1 -1
  69. package/dist/modules/minio.js +14 -4
  70. package/dist/modules/minio.js.map +1 -1
  71. package/package.json +1 -1
  72. package/dist/backend-msb/snapshot-list.d.ts +0 -43
  73. package/dist/backend-msb/snapshot-list.d.ts.map +0 -1
  74. package/dist/backend-msb/snapshot-list.js +0 -53
  75. package/dist/backend-msb/snapshot-list.js.map +0 -1
@@ -158,6 +158,40 @@ export class ReuseFromCheckpointError extends Error {
158
158
  this.name = "ReuseFromCheckpointError";
159
159
  }
160
160
  }
161
+ /**
162
+ * Thrown at `start()` when a container built via `GenericContainer.fromCheckpoint()`
163
+ * carries an env that no longer matches the checkpoint's own captured env —
164
+ * an extra `withEnv()` call, or one overriding a key the checkpoint already
165
+ * set — and the active backend has no way to apply that at restore time.
166
+ *
167
+ * msb is the one backend this affects: 0.7.1 replaced `run --from-snapshot`
168
+ * with a dedicated `msb restore` command that has no `-e`/`--env` flag at
169
+ * all (a disk-only restore replays the sandbox's own captured
170
+ * configuration). Before 0.7.1, an overridden or extended env silently
171
+ * reached the restored guest via `run -e`; since there is now no msb
172
+ * invocation this library could emit that would honor it, silently dropping
173
+ * it would let the workload boot with env the caller never asked for and
174
+ * without env it did ask for — a correctness bug, not a graceful
175
+ * degradation — so this throws instead, before any backend call. Restoring
176
+ * with the checkpoint's own env completely unchanged never hits this: only
177
+ * a call that actually changes what would be sent does. docker is
178
+ * unaffected — restoring there is an ordinary `docker create`/`run` against
179
+ * the committed image with a fresh env array, so this is never thrown when
180
+ * the active backend is `"docker"`.
181
+ */
182
+ export class CheckpointRestoreEnvOverrideError extends Error {
183
+ backend;
184
+ constructor(
185
+ /** The active backend's name — always `"microsandbox"` today, the one backend this affects. */
186
+ backend) {
187
+ super(`fromCheckpoint(...).withEnv(...) cannot override or extend a checkpoint's captured env when restoring ` +
188
+ `on the '${backend}' backend — its restore command has no way to inject env at boot time, so an ` +
189
+ `overridden or extended env would be silently dropped instead of reaching the restored workload. ` +
190
+ `Restore with the checkpoint's own env unchanged, or bake the desired env into a fresh checkpoint instead.`);
191
+ this.backend = backend;
192
+ this.name = "CheckpointRestoreEnvOverrideError";
193
+ }
194
+ }
161
195
  /**
162
196
  * Thrown by `copyFileToContainer`/`copyContentToContainer`/
163
197
  * `copyFileFromContainer` when `containerPath` is not absolute — both
@@ -326,4 +360,31 @@ export class TmpfsRootCheckpointError extends Error {
326
360
  this.name = "TmpfsRootCheckpointError";
327
361
  }
328
362
  }
363
+ /**
364
+ * Thrown restoring a microsandbox checkpoint whose source container had no
365
+ * explicit command (the image's own default entrypoint was running) and
366
+ * whose registry entry carries no captured workload cmdline either —
367
+ * `msb restore` boots a restored sandbox with only its guest agent inside
368
+ * and never re-runs anything on its own (EMPIRICALLY VERIFIED against msb
369
+ * 0.7.1 — see `MsbCliBackend.bootRestoreOnce`'s own doc), so without EITHER
370
+ * source this library has no workload argv left to revive. Thrown instead of
371
+ * silently booting the sandbox idle — the checkpoint either predates
372
+ * workload-cmdline capture, or its capture attempt failed at checkpoint time
373
+ * (see `CheckpointRegistryEntry.capturedCommand`'s own doc on why a capture
374
+ * failure never fails the checkpoint that created it, only a later restore
375
+ * of it). Only ever thrown by the microsandbox backend.
376
+ */
377
+ export class CheckpointWorkloadCommandMissingError extends Error {
378
+ ref;
379
+ constructor(
380
+ /** The checkpoint ref being restored. */
381
+ ref) {
382
+ super(`sandbox restore from checkpoint '${ref}' has no explicit command and no captured workload cmdline in ` +
383
+ "its registry entry — this checkpoint predates workload-cmdline capture, or its capture attempt " +
384
+ "failed at checkpoint time, so there is no workload argv left to revive. Re-checkpoint the source " +
385
+ "container under a build that captures one.");
386
+ this.ref = ref;
387
+ this.name = "CheckpointWorkloadCommandMissingError";
388
+ }
389
+ }
329
390
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"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"}
@@ -40,6 +40,15 @@ export declare class GenericContainer implements AsyncDisposable, NetworkMember
40
40
  private requireIsolationRequested;
41
41
  private checkpointRef;
42
42
  private checkpointSourceBackend;
43
+ /**
44
+ * `cp.spec.env` exactly as captured, set once by `fromCheckpoint()` and
45
+ * never touched again — kept separate from the mutable `envPairs` so
46
+ * `start()` can tell "the caller re-applied the checkpoint's own env
47
+ * unchanged" from "the caller actually overrode or extended it" (see
48
+ * `CheckpointRestoreEnvOverrideError`). `undefined` for every container
49
+ * not built via `fromCheckpoint()`.
50
+ */
51
+ private checkpointCapturedEnv;
43
52
  private handle;
44
53
  private backend;
45
54
  private mappedPorts;
@@ -61,7 +70,14 @@ export declare class GenericContainer implements AsyncDisposable, NetworkMember
61
70
  * captures. `start()` throws `CheckpointBackendMismatchError` before any
62
71
  * backend call if the active backend isn't the one that created `cp`, and
63
72
  * `ReuseFromCheckpointError` if this container is also marked
64
- * `withReuse()` — reuse's identity hash never covers a checkpoint ref.
73
+ * `withReuse()` — reuse's identity hash never covers a checkpoint ref. On
74
+ * msb specifically, `start()` also throws `CheckpointRestoreEnvOverrideError`
75
+ * before any backend call if a further `withEnv()` call actually changes
76
+ * the env beyond what `cp.spec.env` captured — msb's restore command has
77
+ * no way to inject env at boot time (see that error's own doc); re-calling
78
+ * `withEnv()` with the checkpoint's own captured values, unchanged, never
79
+ * throws. docker is unaffected — an overridden/extended env reaches a
80
+ * docker restore normally.
65
81
  * Once started, a restored container is ordinary in every other respect:
66
82
  * fresh host ports, normal reaping-ledger tracking, normal stop.
67
83
  */
@@ -292,21 +308,48 @@ export declare class GenericContainer implements AsyncDisposable, NetworkMember
292
308
  * state error (same shape as `exec`/`logs`) if this container isn't
293
309
  * currently running.
294
310
  *
295
- * Passing `name` makes this checkpoint NAMED and durable: the ref becomes
296
- * deterministic (an absolute `<cacheDir>/checkpoints/rz-ckpt-<name>` path
297
- * on microsandbox, `rightsize/checkpoint:<name>` on docker — see
311
+ * Passing `name` makes this checkpoint NAMED and durable: on docker the ref
312
+ * becomes deterministic (`rightsize/checkpoint:<name>` — see
298
313
  * `checkpointRef` — instead of a random 12-hex suffix), and — only once
299
314
  * the backend call below has actually succeeded — a registry entry is written under
300
315
  * `<cacheDir>/checkpoints/<name>.json` that `Checkpoints.find`/`list`/
301
316
  * `remove` can rediscover later, in this process or a different one. `name`
302
317
  * must match `^[a-z0-9][a-z0-9-]{0,40}$`; an invalid name throws
303
318
  * `InvalidCheckpointNameError` before any backend call. Checkpointing under
304
- * a name that already has a registry entry REPLACES it: the ref is the
305
- * same deterministic value either way, so this best-effort clears the old
306
- * artifact under that ref before creating the new one, then overwrites the
307
- * registry entry — the latest checkpoint under a name always wins.
308
- * Omitting `name` keeps the original behavior byte-for-byte: a random ref,
309
- * no registry entry, ephemeral.
319
+ * a name that already has a registry entry REPLACES it: this best-effort
320
+ * clears whatever artifact the PRIOR checkpoint under `name` actually left
321
+ * behind, then overwrites the registry entry with the BACKEND'S OWN
322
+ * EFFECTIVE ref for the fresh checkpoint — the latest checkpoint under a
323
+ * name always wins. The prior artifact's real location comes from the
324
+ * existing registry entry itself (when one exists for a backend matching
325
+ * the currently active one) rather than a recomputed nominal ref: on
326
+ * docker the two are always the same deterministic value anyway, but on
327
+ * microsandbox (since 0.7.1) the effective ref is a content-addressed
328
+ * artifact path the nominal one never predicts (see
329
+ * `SandboxBackend.createCheckpoint`'s own doc), so reading it back from the
330
+ * registry is what makes this reliable there too — the same lookup
331
+ * `Checkpoints.remove(name)` itself uses.
332
+ *
333
+ * The PRIOR entry's real ref is only ever removed once the fresh
334
+ * checkpoint below has actually been created successfully (and, on a
335
+ * backend whose checkpoint reboots the workload, once the post-checkpoint
336
+ * re-wait below has succeeded too) — never before. When the prior ref and
337
+ * the freshly-minted nominal one are the SAME deterministic value (always
338
+ * true on docker), removing it earlier would just be removing the nominal
339
+ * ref that `createCheckpoint` is about to reuse anyway, so this method
340
+ * best-effort clears the nominal ref up front instead (a harmless no-op on
341
+ * microsandbox, where a name never predicts a real artifact); when the two
342
+ * differ (microsandbox's content-addressed case, or the rare coincidence
343
+ * of the two colliding on any backend), the prior ref is left completely
344
+ * untouched until the new one is confirmed both real and reachable. That
345
+ * ordering is what keeps a FAILED same-name re-checkpoint from destroying
346
+ * an otherwise-good, previously-restorable checkpoint: on any backend, a
347
+ * `createCheckpoint()` (or post-checkpoint re-wait) that throws leaves both
348
+ * the prior artifact and its registry entry completely untouched, exactly
349
+ * as if the failed call had never been attempted. Omitting `name` keeps
350
+ * the original behavior byte-for-byte: an ephemeral checkpoint with no
351
+ * registry entry, whose `Checkpoint.ref` is always the backend's own
352
+ * effective ref.
310
353
  *
311
354
  * On a backend whose `capabilities.checkpointRestartsWorkload` is `true`
312
355
  * (microsandbox: the stop/snapshot/reboot cycle boots a fresh microVM),
@@ -323,6 +366,11 @@ export declare class GenericContainer implements AsyncDisposable, NetworkMember
323
366
  * BEFORE the wait-strategy re-run — the wait must gate a container that is
324
367
  * fully re-linked, not just booted.
325
368
  *
369
+ * If that re-wait (or the link re-install before it) fails, this container
370
+ * is torn down to completion via `stop()` — same as a failed `start()` —
371
+ * before the rejection propagates, so a revived-but-never-ready workload
372
+ * never sits around as an orphan.
373
+ *
326
374
  * Checkpoints are never auto-reaped (a committed image or a disk snapshot
327
375
  * is not a container) — see the checkpoints guide for the manual cleanup
328
376
  * one-liners and the `Checkpoints.remove` cleanup affordance.
@@ -1 +1 @@
1
- {"version":3,"file":"generic-container.d.ts","sourceRoot":"","sources":["../../src/core/generic-container.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generic-container.d.ts","sourceRoot":"","sources":["../../src/core/generic-container.ts"],"names":[],"mappings":"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"}
@@ -4,11 +4,12 @@ import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
  import { FreePorts } from "./free-ports.js";
6
6
  import { RunId } from "./run-id.js";
7
+ import { nextSandboxName } from "./sandbox-name.js";
7
8
  import { Network } from "./network.js";
8
- import { PortBindConflictError, IsolationRequiredError, CheckpointUnsupportedError, CheckpointBackendMismatchError, ReuseFromCheckpointError, RelativeContainerPathError, BackendError, RootDiskConflictError, TmpfsRootExceedsMemoryError, NetworkDisabledConflictError, TmpfsRootCheckpointError, } from "./errors.js";
9
+ import { PortBindConflictError, IsolationRequiredError, CheckpointUnsupportedError, CheckpointBackendMismatchError, ReuseFromCheckpointError, CheckpointRestoreEnvOverrideError, RelativeContainerPathError, BackendError, RootDiskConflictError, TmpfsRootExceedsMemoryError, NetworkDisabledConflictError, TmpfsRootCheckpointError, } from "./errors.js";
9
10
  import { requireValidCheckpointName } from "./checkpoint/name.js";
10
11
  import { checkpointRef } from "./checkpoint/ref.js";
11
- import { toCheckpointRegistrySpec, writeCheckpointRegistryAtomic } from "./checkpoint/registry.js";
12
+ import { toCheckpointRegistrySpec, writeCheckpointRegistryAtomic, readCheckpointRegistry } from "./checkpoint/registry.js";
12
13
  import { Wait } from "./wait.js";
13
14
  import { registerSyncCleanup, unregisterSyncCleanup } from "./cleanup.js";
14
15
  import { Backends } from "./backends.js";
@@ -24,12 +25,29 @@ function requireAbsoluteContainerPath(containerPath) {
24
25
  throw new RelativeContainerPathError(containerPath);
25
26
  }
26
27
  }
27
- const MAX_START_ATTEMPTS = 5;
28
- let sequence = 0;
29
- function nextSequence() {
30
- sequence += 1;
31
- return sequence;
28
+ /**
29
+ * Order-insensitive equality for env pairs — `start()`'s only use is telling
30
+ * "the checkpoint's own captured env, re-applied unchanged" from "the caller
31
+ * actually changed it" (see `CheckpointRestoreEnvOverrideError`). Compared as
32
+ * key/value sets rather than positionally: `withEnv()` re-setting an
33
+ * existing key moves it to the end of `envPairs` (last-write-wins,
34
+ * insertion-ordered — see `withEnv()`'s own doc), which must not by itself
35
+ * read as a change.
36
+ */
37
+ function envPairsEqual(a, b) {
38
+ const aMap = new Map(a);
39
+ const bMap = new Map(b);
40
+ if (aMap.size !== bMap.size) {
41
+ return false;
42
+ }
43
+ for (const [key, value] of aMap) {
44
+ if (bMap.get(key) !== value) {
45
+ return false;
46
+ }
47
+ }
48
+ return true;
32
49
  }
50
+ const MAX_START_ATTEMPTS = 5;
33
51
  /** Typed-first, string-fallback classification: walk the cause chain for a typed conflict, else match the daemon's own wording. */
34
52
  function isPortBindConflict(err) {
35
53
  let current = err;
@@ -111,6 +129,15 @@ export class GenericContainer {
111
129
  requireIsolationRequested = false;
112
130
  checkpointRef;
113
131
  checkpointSourceBackend;
132
+ /**
133
+ * `cp.spec.env` exactly as captured, set once by `fromCheckpoint()` and
134
+ * never touched again — kept separate from the mutable `envPairs` so
135
+ * `start()` can tell "the caller re-applied the checkpoint's own env
136
+ * unchanged" from "the caller actually overrode or extended it" (see
137
+ * `CheckpointRestoreEnvOverrideError`). `undefined` for every container
138
+ * not built via `fromCheckpoint()`.
139
+ */
140
+ checkpointCapturedEnv;
114
141
  handle;
115
142
  backend;
116
143
  mappedPorts = new Map();
@@ -136,7 +163,14 @@ export class GenericContainer {
136
163
  * captures. `start()` throws `CheckpointBackendMismatchError` before any
137
164
  * backend call if the active backend isn't the one that created `cp`, and
138
165
  * `ReuseFromCheckpointError` if this container is also marked
139
- * `withReuse()` — reuse's identity hash never covers a checkpoint ref.
166
+ * `withReuse()` — reuse's identity hash never covers a checkpoint ref. On
167
+ * msb specifically, `start()` also throws `CheckpointRestoreEnvOverrideError`
168
+ * before any backend call if a further `withEnv()` call actually changes
169
+ * the env beyond what `cp.spec.env` captured — msb's restore command has
170
+ * no way to inject env at boot time (see that error's own doc); re-calling
171
+ * `withEnv()` with the checkpoint's own captured values, unchanged, never
172
+ * throws. docker is unaffected — an overridden/extended env reaches a
173
+ * docker restore normally.
140
174
  * Once started, a restored container is ordinary in every other respect:
141
175
  * fresh host ports, normal reaping-ledger tracking, normal stop.
142
176
  */
@@ -144,6 +178,7 @@ export class GenericContainer {
144
178
  const container = new GenericContainer(cp.ref);
145
179
  container.checkpointRef = cp.ref;
146
180
  container.checkpointSourceBackend = cp.backend;
181
+ container.checkpointCapturedEnv = cp.spec.env;
147
182
  for (const [key, value] of cp.spec.env) {
148
183
  container.withEnv(key, value);
149
184
  }
@@ -466,6 +501,20 @@ export class GenericContainer {
466
501
  if (this.checkpointSourceBackend !== undefined && this.checkpointSourceBackend !== backend.name) {
467
502
  throw new CheckpointBackendMismatchError(this.checkpointSourceBackend, backend.name);
468
503
  }
504
+ // Also before any backend work: msb's restore command has no -e/--env
505
+ // flag at all (see CheckpointRestoreEnvOverrideError's own doc) — an env
506
+ // the caller has genuinely changed beyond the checkpoint's own captured
507
+ // values can never reach a restored msb sandbox, so this refuses rather
508
+ // than silently booting it without the env the caller asked for. Only
509
+ // msb is checked: docker's restore is an ordinary create/run against the
510
+ // committed image, so an overridden env reaches it normally. Re-applying
511
+ // the checkpoint's own captured env unchanged never throws — only a call
512
+ // that actually changes what would be sent does.
513
+ if (this.checkpointRef !== undefined &&
514
+ backend.name === "microsandbox" &&
515
+ !envPairsEqual(this.checkpointCapturedEnv ?? [], this.envPairs)) {
516
+ throw new CheckpointRestoreEnvOverrideError(backend.name);
517
+ }
469
518
  // Only the env-resolved path (never an explicit withBackend() override)
470
519
  // drives the reaper: writes this process's run record, sweeps dead runs
471
520
  // on first use, and spawns the watchdog. Skipping it for overrides is
@@ -516,7 +565,7 @@ export class GenericContainer {
516
565
  try {
517
566
  for (let attempt = 0; attempt < MAX_START_ATTEMPTS; attempt++) {
518
567
  const ports = await this.allocatePorts();
519
- const name = `rz-${RunId.value}-${nextSequence()}`;
568
+ const name = nextSandboxName();
520
569
  const spec = this.buildSpec(name, ports);
521
570
  // Appended BEFORE create() — the ledger's `.sandboxes` file is always
522
571
  // a superset of this run's live sandboxes, never a subset. `keepAlive`
@@ -912,21 +961,48 @@ export class GenericContainer {
912
961
  * state error (same shape as `exec`/`logs`) if this container isn't
913
962
  * currently running.
914
963
  *
915
- * Passing `name` makes this checkpoint NAMED and durable: the ref becomes
916
- * deterministic (an absolute `<cacheDir>/checkpoints/rz-ckpt-<name>` path
917
- * on microsandbox, `rightsize/checkpoint:<name>` on docker — see
964
+ * Passing `name` makes this checkpoint NAMED and durable: on docker the ref
965
+ * becomes deterministic (`rightsize/checkpoint:<name>` — see
918
966
  * `checkpointRef` — instead of a random 12-hex suffix), and — only once
919
967
  * the backend call below has actually succeeded — a registry entry is written under
920
968
  * `<cacheDir>/checkpoints/<name>.json` that `Checkpoints.find`/`list`/
921
969
  * `remove` can rediscover later, in this process or a different one. `name`
922
970
  * must match `^[a-z0-9][a-z0-9-]{0,40}$`; an invalid name throws
923
971
  * `InvalidCheckpointNameError` before any backend call. Checkpointing under
924
- * a name that already has a registry entry REPLACES it: the ref is the
925
- * same deterministic value either way, so this best-effort clears the old
926
- * artifact under that ref before creating the new one, then overwrites the
927
- * registry entry — the latest checkpoint under a name always wins.
928
- * Omitting `name` keeps the original behavior byte-for-byte: a random ref,
929
- * no registry entry, ephemeral.
972
+ * a name that already has a registry entry REPLACES it: this best-effort
973
+ * clears whatever artifact the PRIOR checkpoint under `name` actually left
974
+ * behind, then overwrites the registry entry with the BACKEND'S OWN
975
+ * EFFECTIVE ref for the fresh checkpoint — the latest checkpoint under a
976
+ * name always wins. The prior artifact's real location comes from the
977
+ * existing registry entry itself (when one exists for a backend matching
978
+ * the currently active one) rather than a recomputed nominal ref: on
979
+ * docker the two are always the same deterministic value anyway, but on
980
+ * microsandbox (since 0.7.1) the effective ref is a content-addressed
981
+ * artifact path the nominal one never predicts (see
982
+ * `SandboxBackend.createCheckpoint`'s own doc), so reading it back from the
983
+ * registry is what makes this reliable there too — the same lookup
984
+ * `Checkpoints.remove(name)` itself uses.
985
+ *
986
+ * The PRIOR entry's real ref is only ever removed once the fresh
987
+ * checkpoint below has actually been created successfully (and, on a
988
+ * backend whose checkpoint reboots the workload, once the post-checkpoint
989
+ * re-wait below has succeeded too) — never before. When the prior ref and
990
+ * the freshly-minted nominal one are the SAME deterministic value (always
991
+ * true on docker), removing it earlier would just be removing the nominal
992
+ * ref that `createCheckpoint` is about to reuse anyway, so this method
993
+ * best-effort clears the nominal ref up front instead (a harmless no-op on
994
+ * microsandbox, where a name never predicts a real artifact); when the two
995
+ * differ (microsandbox's content-addressed case, or the rare coincidence
996
+ * of the two colliding on any backend), the prior ref is left completely
997
+ * untouched until the new one is confirmed both real and reachable. That
998
+ * ordering is what keeps a FAILED same-name re-checkpoint from destroying
999
+ * an otherwise-good, previously-restorable checkpoint: on any backend, a
1000
+ * `createCheckpoint()` (or post-checkpoint re-wait) that throws leaves both
1001
+ * the prior artifact and its registry entry completely untouched, exactly
1002
+ * as if the failed call had never been attempted. Omitting `name` keeps
1003
+ * the original behavior byte-for-byte: an ephemeral checkpoint with no
1004
+ * registry entry, whose `Checkpoint.ref` is always the backend's own
1005
+ * effective ref.
930
1006
  *
931
1007
  * On a backend whose `capabilities.checkpointRestartsWorkload` is `true`
932
1008
  * (microsandbox: the stop/snapshot/reboot cycle boots a fresh microVM),
@@ -943,6 +1019,11 @@ export class GenericContainer {
943
1019
  * BEFORE the wait-strategy re-run — the wait must gate a container that is
944
1020
  * fully re-linked, not just booted.
945
1021
  *
1022
+ * If that re-wait (or the link re-install before it) fails, this container
1023
+ * is torn down to completion via `stop()` — same as a failed `start()` —
1024
+ * before the rejection propagates, so a revived-but-never-ready workload
1025
+ * never sits around as an orphan.
1026
+ *
946
1027
  * Checkpoints are never auto-reaped (a committed image or a disk snapshot
947
1028
  * is not a container) — see the checkpoints guide for the manual cleanup
948
1029
  * one-liners and the `Checkpoints.remove` cleanup affordance.
@@ -968,33 +1049,147 @@ export class GenericContainer {
968
1049
  throw new TmpfsRootCheckpointError();
969
1050
  }
970
1051
  const ref = checkpointRef(backend.name, name);
1052
+ // Replace semantics: a prior checkpoint under this same name — if any —
1053
+ // is cleared once the new one is confirmed to actually exist (see the
1054
+ // deferred removal after `createCheckpoint`/the post-checkpoint re-wait
1055
+ // below), never before — a failed re-checkpoint must never cost this
1056
+ // container its previously-good, restorable checkpoint. The RELIABLE way
1057
+ // to find where the prior checkpoint actually lives is the registry's
1058
+ // own recorded entry (the same lookup `Checkpoints.remove(name)` uses),
1059
+ // not a freshly-minted nominal ref: on docker the ref is deterministic
1060
+ // from `name`, so the registry entry's `ref` and the nominal one below
1061
+ // are always the same value anyway, but on microsandbox (since 0.7.1)
1062
+ // the EFFECTIVE ref `createCheckpoint` returns is a content-addressed
1063
+ // artifact path the nominal one never determines (see
1064
+ // `SandboxBackend.createCheckpoint`'s own doc) — only the registry's
1065
+ // recorded ref points at the real artifact there. Only consulted when
1066
+ // the entry's own recorded backend matches the CURRENTLY active one — a
1067
+ // foreign-backend ref means nothing to this backend's `removeCheckpoint`,
1068
+ // same gate `find`/`remove` apply to the same mismatch. A missing or
1069
+ // corrupt registry read is treated the same as "no entry" (best-effort:
1070
+ // this must never fail the checkpoint itself), leaving only the
1071
+ // nominal-ref removal below to run.
1072
+ let priorRealRef;
971
1073
  if (name !== undefined) {
972
- // Replace semantics: the ref is deterministic from `name`, so a prior
973
- // checkpoint under this same name — if any — sits under this exact
974
- // ref. Best-effort clear it before creating the new one.
1074
+ try {
1075
+ const registryRead = await readCheckpointRegistry(cacheDir(), name);
1076
+ if (registryRead.kind === "found" && registryRead.entry.backend === backend.name) {
1077
+ priorRealRef = registryRead.entry.ref;
1078
+ }
1079
+ }
1080
+ catch {
1081
+ // Swallowed deliberately — see the comment above.
1082
+ }
1083
+ // Best-effort clear the freshly-minted NOMINAL ref up front, before
1084
+ // creating the new checkpoint — exactly what this method did before
1085
+ // the registry lookup above existed. On docker this nominal ref IS the
1086
+ // prior checkpoint's real recorded ref (deterministic from `name`), so
1087
+ // this is the only removal that ever runs there, and it must run here:
1088
+ // `createCheckpoint` is about to reuse this exact ref, so there is no
1089
+ // "after" at which removing it wouldn't also remove the checkpoint
1090
+ // just created. On microsandbox this is a no-op against a path that
1091
+ // was never real, same as before this method ever read the registry —
1092
+ // so nothing new is destroyed here before the new checkpoint's success
1093
+ // is confirmed.
975
1094
  await swallow(() => backend.removeCheckpoint(ref));
976
1095
  }
977
- await backend.createCheckpoint(handle, ref);
1096
+ // The EFFECTIVE ref: on docker this is always `ref` itself; on
1097
+ // microsandbox (since 0.7.1) it is whatever real artifact path
1098
+ // `createCheckpoint` discovered from `msb snapshot create`'s own stdout
1099
+ // — never necessarily `ref`. Everything downstream (the registry entry,
1100
+ // the returned `Checkpoint`) uses this, not the working `ref`.
1101
+ //
1102
+ // Captured BEFORE the call: a backend whose checkpoint reboots the
1103
+ // workload under a fresh identity (msb's `createCheckpoint`, see its own
1104
+ // doc) mutates `handle.id` IN PLACE once that reboot succeeds — the same
1105
+ // carve-out `SandboxBackend.createCheckpoint`'s own interface doc
1106
+ // documents. `registerSyncCleanup` (see start(), above) keyed its exit-path
1107
+ // teardown entry by the OLD id; if the id just changed, that registration
1108
+ // must be re-keyed to the new one too, or the old entry leaks in
1109
+ // `cleanup.ts`'s registry forever (stop() below only ever unregisters by
1110
+ // the CURRENT `handle.id`, which after a rename no longer matches what
1111
+ // was registered) — silently breaking both the exit-path teardown for
1112
+ // this container AND `liveContainers()`/diagnostics, which read that same
1113
+ // registry.
1114
+ const preCheckpointHandleId = handle.id;
1115
+ const effectiveRef = await backend.createCheckpoint(handle, ref);
1116
+ if (handle.id !== preCheckpointHandleId && !handle.spec.keepAlive) {
1117
+ // Mirrors start()'s own keepAlive exclusion: a keepAlive container was
1118
+ // never registered in the first place, so there is nothing to re-key.
1119
+ // The cleanup closure below reads `handle.id` dynamically (see
1120
+ // start()'s own registerSyncCleanup call), so re-registering it under
1121
+ // the new id is automatically correct without capturing anything else.
1122
+ unregisterSyncCleanup(preCheckpointHandleId);
1123
+ registerSyncCleanup(handle, backend, () => backend.cleanupSync(handle.id));
1124
+ }
978
1125
  if (backend.capabilities.checkpointRestartsWorkload) {
979
- if (this.installedNetworkLinks.length > 0) {
980
- await backend.installNetworkLinks(handle, this.installedNetworkLinks);
1126
+ // The backend checkpoint call above already succeeded — the artifact
1127
+ // exists — but the reboot it triggered can still leave this container
1128
+ // unusable: a revived workload that never becomes ready. Same shape as
1129
+ // start()'s own post-boot wait (line ~722): a failure here must not
1130
+ // leak the freshly-spawned workload (msb's revived `exec` child sits
1131
+ // in `handle`'s attached slot from the moment the reboot reaches
1132
+ // Running, well before this wait ever resolves). Tear this container
1133
+ // down to completion via the same this.stop() start() uses, so the
1134
+ // handle's attached child — and the sandbox itself — never outlive
1135
+ // this rejection, before rethrowing.
1136
+ try {
1137
+ if (this.installedNetworkLinks.length > 0) {
1138
+ await backend.installNetworkLinks(handle, this.installedNetworkLinks);
1139
+ }
1140
+ await this.waitStrategy.waitUntilReady(this.asWaitTarget());
1141
+ }
1142
+ catch (err) {
1143
+ await this.stop();
1144
+ throw err;
981
1145
  }
982
- await this.waitStrategy.waitUntilReady(this.asWaitTarget());
983
1146
  }
1147
+ // The workload argv the backend captured from the guest during the
1148
+ // createCheckpoint() call just above, when `handle.spec.command` was
1149
+ // undefined — see `SandboxBackend.capturedWorkloadCommand`'s own doc.
1150
+ // `undefined` on every backend that never captures one (docker, or msb
1151
+ // when the source container already carried an explicit command).
1152
+ const capturedCommand = backend.capturedWorkloadCommand?.(handle);
984
1153
  if (name !== undefined) {
1154
+ // Only reached once the fresh checkpoint above is confirmed both real
1155
+ // (createCheckpoint succeeded) and reachable (the post-checkpoint
1156
+ // re-wait above, when applicable, succeeded too) — a failure at either
1157
+ // point already threw and returned above, before any of this ran. The
1158
+ // PRIOR checkpoint's real recorded ref (see the registry lookup
1159
+ // before createCheckpoint) is best-effort removed here, now that it is
1160
+ // safe to: never when it's the SAME ref as the nominal one already
1161
+ // cleared above (docker — clearing it again here would target the
1162
+ // fresh checkpoint `createCheckpoint` just reused that exact ref for),
1163
+ // and never when it happens to equal the fresh checkpoint's own
1164
+ // EFFECTIVE ref (the rare case of two checkpoints content-addressing
1165
+ // to the very same artifact on microsandbox — removing it would delete
1166
+ // the checkpoint this call just created, not a prior one).
1167
+ if (priorRealRef !== undefined && priorRealRef !== ref && priorRealRef !== effectiveRef) {
1168
+ const realRef = priorRealRef;
1169
+ await swallow(() => backend.removeCheckpoint(realRef));
1170
+ }
985
1171
  // Only after the backend checkpoint above has actually succeeded — a
986
1172
  // failed createCheckpoint() already threw, so a registry entry is
987
1173
  // never written for a checkpoint that doesn't exist.
988
1174
  const entry = {
989
1175
  name,
990
- ref,
1176
+ ref: effectiveRef,
991
1177
  backend: backend.name,
992
1178
  createdIso: new Date().toISOString(),
993
1179
  spec: toCheckpointRegistrySpec(handle.spec),
1180
+ ...(capturedCommand !== undefined ? { capturedCommand } : {}),
994
1181
  };
995
1182
  await writeCheckpointRegistryAtomic(cacheDir(), name, entry);
996
1183
  }
997
- return { ref, backend: backend.name, spec: handle.spec };
1184
+ // `handle.spec.command` already covers an explicit command; only
1185
+ // substitute the captured fallback when the source had none, so a later
1186
+ // `fromCheckpoint(cp).start()` on THIS in-memory `Checkpoint` — unnamed
1187
+ // or named, same process or not — already carries the fully-resolved
1188
+ // workload argv a restore-side backend needs to revive it, the same
1189
+ // merge `fromCheckpointRegistryEntry` applies for a registry-mediated
1190
+ // restore.
1191
+ const effectiveSpec = handle.spec.command !== undefined || capturedCommand === undefined ? handle.spec : { ...handle.spec, command: capturedCommand };
1192
+ return { ref: effectiveRef, backend: backend.name, spec: effectiveSpec };
998
1193
  }
999
1194
  /**
1000
1195
  * Copies a host file or directory into this running container at