Skip to content

Add durable snapshot and restore - #354

Open
simongdavies wants to merge 9 commits into
mainfrom
simongdavies-snapshot-and-restore
Open

simongdavies wants to merge 9 commits into
mainfrom
simongdavies-snapshot-and-restore

Conversation

@simongdavies

Copy link
Copy Markdown
Member

Summary

Adds durable OCI-backed snapshot and restore support for runtime-ready and loaded JavaScript sandboxes across Rust and Node.

API changes

Rust

  • JSSandbox now supports snapshot() and restore() before handlers are loaded.
  • LoadedJSSandbox snapshots preserve evaluated handlers and mutable guest state.
  • SandboxBuilder::build_from_snapshot() creates a SandboxRestorer.
  • SandboxRestorer::restore::<JSSandbox>() and restore::<LoadedJSSandbox>() enforce the captured lifecycle type.
  • Host functions, filters, module loaders, and print callbacks are configured on SandboxBuilder for fresh construction and restoration.

Node

  • JSSandbox now exposes snapshot() and restore().
  • SandboxBuilder.buildFromSnapshot() creates a SandboxRestorer.
  • SandboxRestorer.restoreJsSandbox() and restoreLoadedSandbox() restore the matching lifecycle.
  • Snapshot.save(), Snapshot.load(), and Snapshot.loadUnverified() provide OCI persistence.

Breaking changes

  • hyperlight_js::Snapshot now names the lifecycle-aware snapshot wrapper instead of re-exporting Hyperlight’s raw snapshot.
  • LoadedJSSandbox::snapshot() now returns Snapshot instead of Arc<hyperlight_host::Snapshot>.
  • LoadedJSSandbox::restore() now accepts and consumes the lifecycle-aware Snapshot.
  • Code explicitly typed against the old hyperlight_js::Snapshot alias must migrate to either the new wrapper or hyperlight_host::sandbox::snapshot::Snapshot.
  • Host modules are no longer cloneable. Reusable definitions must be factories that create fresh module and callback instances per sandbox.
  • Node ESM no longer exports internal *Wrapper aliases. Import Snapshot, JSSandbox, LoadedJSSandbox, SandboxBuilder, and SandboxRestorer.

Limitations

Hyperlight issue #1870 prevents complete preflight inspection of raw snapshot host requirements. Hyperlight JS records its logical host-function requirements in metadata, while module-loader requirements remain unknown until upstream introspection is available.

@simongdavies simongdavies added kind/enhancement New feature or improvement breaking-change Breaking change requiring consumer migration labels Oct 8, 2026
Comment thread src/js-host-api/src/lib.rs Fixed
Comment thread src/js-host-api/src/lib.rs Dismissed
@simongdavies simongdavies added the ready-for-review PR is ready for (re-)review label Oct 9, 2026
Add lifecycle-aware persistent snapshots for runtime-ready and loaded
JavaScript sandboxes across the Rust and Node APIs. Restore process-local
host resources through SandboxBuilder, keep captured requirements separate
from available host callbacks, and prevent accidental callback sharing.

BREAKING CHANGE: hyperlight_js::Snapshot now names the lifecycle-aware
snapshot wrapper, LoadedJSSandbox snapshot and restore use that wrapper,
HostModule is no longer cloneable, and Node ESM no longer exports internal
Wrapper aliases.

Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
Explain durable and in-memory snapshot lifecycles, host-function factories,
required versus available capabilities, external and native module behavior,
and the current module-loader introspection limitation.

Include Rust and Node restoration examples and migration guidance for the
breaking snapshot API changes.

Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
Clarify that generated GitHub release notes use pull-request labels rather
than commit-message footers, and require the breaking-change label for
released API incompatibilities.

Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
Import the Node URL constructor explicitly now that generated-type scripts are
included in the JavaScript lint gate. Update custom native-module persistence
coverage to use the lifecycle-aware snapshot restorer API.

Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
@simongdavies
simongdavies force-pushed the simongdavies-snapshot-and-restore branch from 41d1fc0 to 627fa93 Compare October 22, 2026 10:25
simongdavies and others added 5 commits October 22, 2026 12:29
Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
Restore a saved snapshot with SandboxBuilder::from_snapshot(snapshot).build(),
matching upstream hyperlight. Remove SandboxRestorer, RestoreTarget, and
build_from_snapshot().

* Snapshot is now Snapshot<T>, where T is JSSandbox or LoadedJSSandbox.
  Loading checks the saved kind once. In-memory snapshot() and restore() use
  Snapshot<Self>, so restoring the wrong kind does not compile. Remove
  SnapshotKind and kind().
* SandboxBuilder is now SandboxBuilder<S = NewSandbox>. Every setter applies
  when restoring. The snapshot's layout overrides heap, scratch, and transport.
  Interrupt, crashdump, and debug settings are forwarded on restore.
* Rename loaders to match upstream: load skips digest checks and checked_load
  verifies. Remove load_unverified. save takes &OciTag and returns OciDigest.
  Re-export OciTag, OciDigest, and OciReference.
* Remove HostFunctionFilter, requirements(), SnapshotRequirements, and
  RequirementStatus. Restore still rejects missing host functions before
  running the guest, and metadata still records them.
* Node: SandboxBuilder.fromSnapshot(snapshot).build() returns a JSSandbox or
  LoadedJSSandbox. Remove SandboxRestorer, buildFromSnapshot,
  setHostFunctionFilter, kind, requirements, and loadUnverified. Add
  checkedLoad. In-memory restore of the wrong kind fails with ERR_INVALID_ARG.
  TypeScript declares SandboxBuilder<T> so fresh builds stay ProtoJSSandbox.
* Remove every generated Wrapper alias before renaming, so declarations no
  longer contain self-aliases such as LoadedJSSandbox = LoadedJSSandbox.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
* Reject snapshot tags ending in .hljs-base. A user tag can no longer replace a
  LoadedJSSandbox baseline tag.
* Record GUEST_ABI_VERSION in snapshot metadata and reject snapshots recorded
  with another version, before running the guest.
* Ignore recorded host modules that have no functions when checking restore
  requirements.
* with_host_function_modules and setHostFunctionModules take the modules
  directly. Each module still moves into one builder.
* Register the host-function manifest in get_loaded_sandbox only after a
  restore replaced guest memory, saving a guest call per load and unload.
* Deprecate ProtoJSSandbox::host_module, register, and set_module_loader, and
  the Node proto.hostModule() and proto.register(), in favor of SandboxBuilder.
* Type Node host callbacks as (...args: any[]) so typed callbacks satisfy
  strictFunctionTypes.
* Store staged script sources as plain strings in snapshot metadata.
* Revert the unrelated .vscode auto-approve entries and source-map-js override,
  and stop exporting __napiBindingTarget from ESM.
* Document the CPU vendor and hypervisor binding, the guest ABI check, the
  reserved tag suffix, the 1 MiB staged-source limit, and ERR_BUSY.
* Test runtime config forwarding on restore, the reserved tag suffix, the 1 MiB
  staged-source limit, the guest ABI check, and empty recorded modules.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
apply_runtime_config took `mut builder` but reassigned it only under Linux
and debug cfgs, so non-Linux builds hit the crate's unused_mut deny. Shadow
the builder per cfg instead. Gate the builder test module on kvm or mshv3,
since its only test needs both Linux interrupt settings.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Restore the unconditional RegisterHostModules call in get_loaded_sandbox and
unload. Skipping it for freshly built sandboxes made the CPU-time monitor tests
fail on macOS debug CI, where they passed before that change. This keeps the
fresh-sandbox call sequence identical to the previous release.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Breaking change requiring consumer migration kind/enhancement New feature or improvement ready-for-review PR is ready for (re-)review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants