OneDrive 1drv.ms links no longer serve raw bytes to headless clients after the SPO migration. The disk image now lives as a release asset on a private repo and is fetched via 'gh release download' using DISK_REPO + DISK_TAG vars and an IMAGES_REPO_TOKEN secret. Scripts fail fast on missing env, bad zip, or missing windows95.img.
* Fix guest TCP recv() stalling under concurrent traffic
v86's fake_network stores TCPConnection routing fields (hsrc/hdest/
psrc/pdest) as zero-copy subarrays of the SYN frame, which is itself a
view into the NE2000 TX ring. Win95's driver uses a 12-slot ring; once
it wraps (any concurrent SMB/NBNS/ping while waiting for an upstream
reply), pump() emits segments with whatever IP now occupies that slot,
the guest RSTs them, and recv() blocks forever.
- libv86.js: copy the four address arrays at TCPConnection construction
(matches felixrieseberg/v86@dd13099c on fake-network-copy-tcp-addrs,
now merged into windows95-base)
- tools/probe-tcp.sh + net/tcp-trace.ts + tcp-relay.ts test stub +
debug-harness WIN95_PROBE_RUN2: end-to-end regression harness
(boot → ping -t → telnet → async write after ring wrap → assert ACK).
All env-gated, no production-path change.
- docs/v86-patches.md: tracker for all fork patches + upstream PR state
- update-v86 SKILL.md: cross-link and new fork-branch entry
* Drop checked-in upstream PR description
Belongs on the GitHub PR, not in the repo.
Swap the unmaintained parcel-bundler@1.x for Vite's build API, called
from the same generateAssets hook. Output layout is unchanged
(dist/src/main/main.js + dist/static/index.html + dist/renderer.{js,css})
so no runtime path changes — __dirname-based asset lookup, loadFile, and
packagerConfig.ignore all keep working.
Renderer is built in lib/CJS mode with Node builtins + electron
externalized; a one-line banner aliases exports=module.exports since the
Electron <script> context provides module/require but not a bare exports
global. CSS (98.css + root.css) is now imported from app.tsx so Vite
emits a single renderer.css with fonts inlined.
Drops parcel-bundler and rimraf (vite-build clears dist/ itself).
~800ms full build.
* Rewrite update-v86.js for the current build pipeline
Both v86 fixes we've been carrying are now real branches on the fork
(PR #1540 electron-renderer-fs-loader, PR #1541 ide-shared-registers)
combined as felixrieseberg/v86:windows95-base. update-v86.js no longer
needs to patch sources at build time — it just builds whatever's checked
out and copies the result.
Gone: the fallback-to-copy.sh path, the skew-day check, the structural
regex patches for load_file/exportSymbol/fetch-bind, the phantom-slave
guard (both are in the branch), the --js-only flag. If you don't have
cargo/clang/java/closure, the script fails loudly — no silent fallbacks.
Added: sanity checks against the installed libv86.js for the invariants
our SMB integration and parcel-build shim depend on, so if upstream
changes something load-bearing we see it as a WARN at update time
instead of a runtime failure.
Tested end-to-end: 5/5 sanity checks, fresh boot SUCCESS in 32s.
* docs and skills: capture SMB/v86/testing knowledge from the session
- docs/smb-share.md: user-facing SMB integration overview (how to mount in
Win95, what's implemented, what's not). Points at the protocol-level
README inside src/renderer/smb/ for wire-level gotchas.
- .claude/skills/probe-win95: how to boot and test the VM without a human.
Env vars, file locations, failure modes, the XT scancode keyboard trick,
bisect rules of thumb.
- .claude/skills/update-v86: how to pull upstream v86 changes, what the
five sanity-check WARNs mean, how to retire the fork branches when the
PRs merge upstream.
.gitignore narrowed to exclude only the runtime dirs (scheduled_tasks.lock,
worktrees) instead of the whole .claude/ tree, so skills can be committed.
Patches src/ide.js before building: restores the dual master+slave writes
for ATA Command Block registers (Features, Sector Count, LBA Low/Mid/High)
that 1b90d2e7 changed to current_interface-only. Those registers are
channel-shared per ATA spec; Win95's ESDI_506.PDR writes them then switches
drive-select expecting the values to persist. Found via JS-only bisect.
Boots fresh in ~32s with the same sporadic-bluescreen rate as the prod build.
The new build has the tcp-connection bus event so SMB uses the clean path
instead of the connection-theft hack (guard added for the old-API monkeypatch
to skip if the bus handler already accepted).
tools/update-v86.js applies the source patch automatically. v86 checkout
left clean (patch stashed).
Windows 95 can now mount a host folder as a network drive at \\HOST\HOST.
Read-only, ~1500 lines, zero deps. Defaults to ~/Downloads, configurable in
Settings.
Protocol: NEGOTIATE (LANMAN2.1), SESSION_SETUP, TREE_CONNECT, TRANSACTION/RAP
(NetShareEnum, NetServerGetInfo, NetWkstaGetInfo), TRANSACTION2/FIND_FIRST2,
SEARCH (8.3 with ~N suffix mapping), OPEN_ANDX, NT_CREATE_ANDX, READ_ANDX,
CLOSE, QUERY_INFORMATION, CHECK_DIRECTORY. NetBIOS Name Service on UDP 137
answers Node Status and Name Query so \\HOST resolves.
v86 hook: monkeypatches adapter.on_tcp_connection (old API), shadows
adapter.receive during a port-80 probe to steal a TCPConnection without
side effects, re-aims it at port 139. Data via .on_data (Closure
dead-code-eliminated .on/.emit). Also registers tcp-connection bus event
for newer v86 builds.
Security: read-only, path traversal blocked lexically and through symlinks
(realpath the deepest existing ancestor, re-append tail, confirm under root).
Share path validated in main-process IPC.
BIOS updated to SeaBIOS 1.16.2 (compatible with old v86). v86 itself stays
on the Feb 2025 prod build — newer builds hang at the splash screen on fresh
boot (bisect tooling included in tools/).
Also: tools/update-v86.js builds wasm+libv86+BIOS from a local v86 checkout
and refuses to install JS/wasm pairs more than 14 days apart (copy.sh ships
mismatched pairs). tools/parcel-build.js dynamic-import patch made tolerant
of post-d4c5fa86 builds.
The previous build patched libv86.js by exact-string match against
Closure-mangled identifiers (k.load_file, H.exportSymbol, pa, qa),
which broke on every upstream rebuild.
Of the three old patches:
- exportSymbol order: now a one-line HTML shim copying module.exports.V86
to window after libv86 loads
- this.fetch binding: fixed upstream
- load_file XHR vs fs: replaced by patching await import('node:fs/promises')
to require('fs').promises - string literals survive Closure, fails loud
if absent
Also adds tools/update-v86.js to pull new builds from copy.sh, and exposes
the renderer DevTools protocol on localhost:9222 in dev.