vfs: preserve lazy artifact integrity across hosts#897
Conversation
Phase B-1 matrix build status —
|
| Package | Arch | Status | Sha |
|---|---|---|---|
| kandelo-sdk | wasm32 | built | 03c5fe40 |
| rootfs | wasm32 | built | 0bbbbc02 |
| shell | wasm32 | built | db390c62 |
| lamp | wasm32 | built | 07ea08f6 |
| node-vfs | wasm32 | built | b2ed8f02 |
| wordpress | wasm32 | built | 77da1ebb |
Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.
|
CI follow-up at exact head The larger Hello response also proved that browser fetch progress chunk counts are transport-specific. The assertion now preserves the exact lifecycle order while coalescing consecutive Local evidence through |
|
Follow-up root-cause fix is now at exact head The staged shell artifact was valid before browser assembly: Exact-head evidence: focused lazy VFS/archive/image suite 92/92; full default host Vitest 892 passed with 29 skipped and 2 expected failures; host declarations built; the exact prepared CI workspace resolved all 91 browser imports; the previously failing Chromium shell and modeset tests both passed. Independent devil review APPROVED after narrowing The earlier Linux |
|
Fresh-head CI at |
Contract
Lazy VFS metadata is authoritative system state. A successful HTTP response must not replace a declared executable with unrelated, truncated, partially stored, or otherwise wrong-sized bytes, and Node/browser hosts must expose the same post-materialization VFS state.
Root cause
The browser runner requested a lazy ABI 18 executable, but Vite returned its 200 HTML fallback for the missing asset.
MemoryFileSystemtrusted HTTP success and replaced the stub without comparing the decoded body to the declared size, so exec failed later as ENOEXEC. Adjacent v1 integrity gaps also allowed malformed section lengths/inode mappings, unbounded declarations and ZIP inflation, short VFS writes to be reported as success, and concurrent resolutions of one inode to race. Node executed lazy bytes out of band without materializing the VFS, diverging from the browser.The first full merge-gate run exposed a second state contradiction. The staged shell image correctly carried
/usr/bin/dashas an empty lazy stub, with/bin/dashas a symlink. Browser live setup then opened/bin/dashwithO_TRUNCand wrote concrete bytes, butMemoryFileSystemretained the inode's lazy metadata. Serializing that impossible combination caused the new strict importer to reject the image. A successfulO_TRUNCnow detaches the resolved inode from its lazy backing. Replacing one member of a lazy archive also removes only that member from the pending group, so materializing a sibling cannot overwrite the local replacement. Truncation to zero has the same safe transition; nonzero truncation is unchanged because it requires preserving the existing prefix.Change
O_TRUNCor truncation to zero.ABI and artifacts
No shared ABI shape or syscall behavior changed.
ABI_VERSIONremains 18 andabi/snapshot.jsonis unchanged. This tightens validation and state transitions for v1 images rather than changing the v1 format. No package binaries or VFS images were rebuilt. The exact staged ABI 18 shell image was inspected before browser boot:/usr/bin/dashwas a valid empty stub with a 459,958-byte declaration, and all 77 lazy entries imported successfully.Validation
./scripts/dev-shell.sh npm --prefix host run buildnpm --prefix host run typecheck./scripts/dev-shell.sh bash scripts/check-abi-version.shgit diff --checkc69948e9c: approved with no remaining blockersThe exact browser evidence above intentionally uses Kandelo's staged ABI 18 release packages because this PR tests platform VFS assembly, not Homebrew bottle pouring. Homebrew Node and Chromium bottle acceptance remains a separate distribution gate. The full repository/conformance matrix and a manual full browser-demo walkthrough were not run locally; CI remains the broader evidence gate. The prior Linux Vitest failure at
teardown-reclaim.test.ts:71is the fixed-delay readiness race addressed independently by #889, not a lazy VFS failure.