# Snapshots `Runtime::create_snapshot(layouts)` captures one context per extension layout in one V8 artifact. `Runtime::from_snapshot(options, &snapshot)` restores the matching layout; `Runtime::new(options)` bootstraps from scratch. ## Static and live state Extension entries run in dependency order. Capture stops before the one-shot `ext:core/bootstrap` `onRuntimeStart` callbacks; cold and restored runtimes both run those callbacks with fresh native state before public exports are registered. Static entries must be deterministic in the target and retain no native resources, pending work, event handlers, or foreign V8 handles. Process metadata, clocks, live-only V8 intrinsics (`SharedArrayBuffer`, `WebAssembly`), and identity-keyed state about the global proxy belong in live initializers. Restoration replaces that proxy, so static WeakMap brands on `globalThis` do not survive. V8 omits `Error.stackTraceLimit` during capture. Core temporarily supplies its default (10) for error locations, then removes it before serialization. Static changes to the limit are rejected because they would not survive restoration. ## Trust and heap ownership - `Snapshot::from_bytes` is unsafe: only the avril container is validated. The artifact must come from the same core sources, target, V8 build, and flags, except for the dedicated generator's random seed. The layout fingerprint checks framed core sources/native catalog, entry order, extension sources, and op names; it is mismatch detection, not validation of untrusted V8 bytes. - Overlapping isolates must share one read-only heap image. All layouts are captured together; a different artifact or cold/restored mixture is rejected with `ReadOnlyHeapInUse` before isolate creation. Sequential use is allowed. Core identifies a heap family by the whole V8 blob's SHA-256; direct `v8` isolates bypass this accounting. - `--no-extensible-ro-snapshot` alone does **not** make independent captures compatible: read-only images can differ in pointer padding and the next SharedFunctionInfo ID. Release V8 silently shares the first image; debug V8's `heap/read-only-spaces.cc::VerifyChecksum` rejects mismatches. Sharing the wrong function-ID counter can also break debugger lookups. - Runtimes on one thread must be used and dropped in reverse creation order. Leases stay pinned if disposal panics before deleting the native isolate. - rusty_v8 cannot dispose unfinished creators. On capture failure, core releases its native state, exits the creator if it is current, and leaks it with a warning. Its built-in heap stays pinned until process exit. Foreign handles or other unserializable resources can make V8 **abort**, not return an error. ## Generation and validation Use a dedicated, short-lived generator process. Call `initialize_snapshot_generator()` before other core use for reproducible artifacts: core records handles in name order, and V8's fixed generator seed controls identity hashes and mmap hints. This mode refuses live runtimes; normal runtime processes are never seeded. Reproducibility assumes the same V8 build/platform and that the kernel honors those hints. This V8 version does not rehash snapshots by default; avril snapshots are not rehashable, so do not enable `--rehash-snapshot`. The CLI's build script uses canonical runtime assembly, guarding live ops except platform and encoding-label queries. It embeds one artifact for main and nested workers, with no runtime cache or cold fallback. Generation requires `HOST == TARGET`, including CLI checks. The runtime build dependency means `cargo check` compiles and runs the host generator, and release builds compile both host and target copies. Library-only checks can use `-p avril-runtime`. Exercise V8's own heap consistency checks as well as ordinary tests: ```sh CARGO_BUILD_JOBS=2 CARGO_TARGET_DIR=/tmp/avril-debug-v8 V8_FORCE_DEBUG=true \ nix shell nixpkgs#cargo-nextest -c cargo nextest run --locked -p avril-core ```