Airstrike ordinary manifest integrity repair — 2026-10-24
This candidate repairs the ordinary-loading gap that rejected producer
fdd1e48dbccb1cfe15c78b8b9f772bd13cc60e7d in review
3899d8f323e20f9c10f044c66dc319590388f645. It starts at main
3f75d7afdfc93819d4874b2cf126ca83e66d5bd2. This is a tested task branch
for independent review, with no main integration or deployment.
Production boundary
Airstrike's registry entry now declares localManifestIntegrity containing
the repository-relative inventory URL and SHA-256 of its exact JSON bytes.
The inventory remains the producer's unchanged 89-file pins, independently
compared to the game subset of the accepted 261-file runtime manifest.
The untrusted local manifest cannot supply or replace these pins.
lib/app-files.js shares one manifest interpretation between synchronous
CLI and asynchronous browser adapters. It requires exactly the 88 companion
rows, each with its exact game/ URL, guest path and size; row ordering is
immaterial. Duplicates, omissions, extra files, alternate guest aliases,
optional flags, INI transformations, external/traversal URLs and installer
registry additions fail closed for this opt-in policy. All 89 files,
including the original EXE and all eight authentic DLLs, must pass size and
SHA-256 checks before any destinations are modified.
The actual test/run.js registered route performs this preflight at the
start of main, before build/PE loading/VFS writes. Both --app and a
canonical-path-matched registered --exe select the policy. The validated
byte snapshots feed EXE reads, DLL seeds and the ordinary VFS mount paths.
An arbitrary external, unregistered --exe retains its existing explicit
input contract; this change does not infer a game identity from a filename.
The actual browser source is lib/browser-shell.js. It resolves and checks
the media before wine.init, then gives the validated EXE bytes to
wine.loadExe and cached bytes to its ordinary DLL resolver. Since
host.loadFiles fetches URLs rather than reading a bytes member, companion
mounts use temporary Blob URLs of the verified snapshots. The ordinary
host loader consumes these immutable bytes; finally revokes the URLs.
host.js itself is unchanged. The pinned policy is checked on every launch,
including when the generic _localFilesResolved flag was already set.
Apps without this opt-in policy retain generic manifest merging, registry
overrides, installer registry metadata and CD track sizes. No binaries,
configuration, sound, image or DLL payloads are committed or redistributed.
The six other registration files are byte-exact copies of the rejected
producer; its lib/apps.js changes are retained with the added policy.
Acceptance and regression evidence
test/test-airstrike-ordinary-manifest.js requires genuine retained inputs;
it has no SKIP or synthetic substitute. It runs byte-exact source extractions
of the production CLI selector, resolver, initial preflight and early VFS
mount loop, and the production browser resolver, DLL resolver and snapshot
handoff. It also executes the actual host.loadFiles method with a minimal
VFS and Blob transport, without instantiating the emulator.
The positive case verifies 89 identities / 4,072,418 bytes, 88 exact companion destinations and all eight browser DLL results. The negative cases reject both reviewer tamper conditions, twelve structural/mapping changes, every one of 89 individually mutated identities, every one of 89 missing files, and an inventory hash mismatch on both routes. Failed preflight leaves destinations/app file state unchanged and cannot reach the mounting blocks. Reads outside the approved fixture/inventory are forbidden by the test. It also changes source bytes after validation to prove the CLI mount/read snapshots and the browser's actual host/DLL loading retain genuine bytes.
Existing Silver CLI launch, candidate browser-manifest writer, local
proprietary dropdown and genuine Airstrike registration tests all pass.
The latter retains its 89 missing-file and five changed-identity controls,
real corpus writer/VFS closure checks and eight DLL seed checks. All Node
commands use old-space 256 MiB, fresh MemAvailable above 805,306,368 bytes,
and CPU/wall limits of 60 seconds. Syntax checks pass for all four changed
production JavaScript files. Final ordinary regression completes in about
two seconds. Full commands, memory readings, exit codes, source/excerpt
hashes and results are adjacent.
The authoritative final regression is ordinary-tests-publish.json with
ordinary-test-publish.log; earlier passing iterations remain retained.
The independent reproduce-manifest-gap.js is retained byte-for-byte.
Its unchanged execution against five exact fdd1e48 source blobs returns
exit 0 with verdict REJECT and both ordinaryLoaderAccepted: true:
this is the expected gap-present finding on the rejected producer.
It is not a repaired-candidate pass. The candidate's separate ordinary
regression explicitly records both acceptance conditions as false for
both routes. The donor's sparse filesystem initially lacked test/run.js;
the retained external failure log documents that lookup, after which exact
Git blobs were materialized only in this task's private rejected-source
snapshot. No donor was modified and the independent script was not adapted.
To rerun the candidate regression from this checkout:
timeout 60s node --max-old-space-size=256 test/test-airstrike-ordinary-manifest.js \
/tmp/airstrike-original-private-oct23/docs/re-notes/airstrike-original-20261023/release/airstrike-pre6a \
/tmp/airstrike-legacy-zlib-private-oct23/docs/re-notes/airstrike-legacy-zlib-20261023/zlib.dll \
/home/user/wine-assembly/test/binaries/dlls/msvcrt.dll
Fresh resource/memory admission and CPU limits remain required. Tests remove
their task-owned temporary genuine closure in finally.
Limits and handoff
This is production-function JavaScript/filesystem qualification, not a full CLI process launch, real browser process, WebAssembly build/instantiation, guest execution, registered gameplay, audio or full-game qualification. Those remain untested here. The browser adapter relies on normal WebCrypto and Blob URL support. The existing accepted historical movement/fire scope is unchanged. No public hosting, deployment, packages, build or box operation occurred. Box ID/state/expiry are N/A; lifecycle remains coordinator-owned.
The private checkout and evidence are retained at
/tmp/airstrike-ordinary-manifest-boundary-repair-oct24/. The measured
62 MiB admission includes the 8 MiB reserve, transport, temporary media,
launcher/session/SQLite and future growth inside 64 MiB. The later ledger
conservatively charges the entire current SQLite files rather than only
positive growth. Internal tool capture is conservatively charged separately
from full retained launcher/session history. Final resource, publication and
cleanup receipts live outside the commit because they bind its resulting
identity. Strict SSH task-branch publication records the actual remote task
and main refs under the shared lock; main is not written. Root tracked
files/index/refs and all donors remain read-only.