# Pod-bay deployment plan (network-agnostic archive) How the two emulated titles (BattleTech + Red Planet, the only DOSBox games) ship into the existing TeslaConsole / TeslaLauncher pod-bay architecture as a self-contained, air-gapped, statically-addressed install. Everything here is settled unless marked **OPEN**. **Built so far:** the supervising launch entry-point (`pod-launch/`, C# Job-Object supervisor -- kill-cascade verified) and the install-side artifacts (`deploy/`: `postinstall.bat`, `configure.ps1`, `net_*.conf.tmpl` -- rendering + NIC-bind + `bayIP+100` + WATTCP-stamp verified against a scratch tree). Remaining build work is in the OPEN list. ## The existing contract (do NOT change it) - A pod bay is a Windows PC on a **self-contained, air-gapped network** (no venue LAN, no internet) shared by the console + up to **32 pods** (~140 units left worldwide -- a bounded preservation fleet, not a scaling problem). - **All addresses are static, assigned by TeslaConsole.** No DHCP. - **TeslaConsole -> TeslaLauncher**: transmits a zip; the zip has ONE folder + a `postinstall.bat` at its root; TeslaLauncher extracts to the games root (`C:\games` almost always) and runs `postinstall.bat` **elevated**. - **TeslaConsole registers/invokes the launch** -- it tells TeslaLauncher to execute a command in the game dir with args (args select game/mode). `postinstall.bat` does NOT register anything. - **TeslaLauncher also STOPS the app** -- e.g. on a console "kill" call when a DOSBox hangs. Its control path must stay alive independent of DOSBox. - Only **BT/RP 4.10 are emulated** (DOSBox); the rest of the catalog runs **native** (preservation project). For every native title, TeslaLauncher and the game share the bay's single IP as one process space; **these two are the only exception**, because the game's TCP stack lives inside DOSBox, separate from the host. ## Network model -- THE core of this plan ### Why a bridge, not slirp/NAT (settled, source-proven) The pods form a **direct peer-to-peer TCP mesh**, not star-through-console: each pod `TCP_OPEN`s to the other pods' real IPs from the egg roster (`MasterMode`/`SlaveMode` decides who dials), tracked separately from the console stream. Evidence: `NETNUB_TCP_OPEN` ("Opens a TCP stream to another computer", NETNUB.HPP), `OpenConnection(...,int internet_address)`, `NetworkStartupMode{SlaveMode,MasterMode}`, `numberOfMungaHostsConnected` vs `numberOfConsoleHostsConnected` (L4NET.HPP). See memory `pod-multiplayer-mesh`. => NAT can't carry a pod dialing a same-subnet peer, so the emulated NIC must be a **real bridged host** on the segment. ### Two IPs per bay -- launcher keeps the bay IP; the DOS game gets `bayIP+100` The launcher's IP never moves. The DOSBox guest gets a **second** address so it can be a first-class host without stealing the bay IP: | Role | Address | Owner | Used for | |---|---|---|---| | **Bay / control IP** | e.g. `10.0.0.5` (static, console-assigned) | host Windows / TeslaLauncher | zip, launch, **kill** -- unchanged from native titles; alive even if DOSBox hangs | | **Game IP** | `bayIP + 100` = `10.0.0.105` | DOSBox guest (bridged, own emulated-NIC MAC) | egg identity, mesh peer target, `:1501` | Both live at once on the **one** physical NIC (host MAC = bay IP, emulated NE2000 MAC = game IP). The `+100` offset on the last octet is safe forever at this scale: 32 bays fit in `.1-.32`, games in `.101-.132`, no wrap, no collision. ### The `+100` convention -- two edits, both derive the same value 1. **`postinstall.bat`** reads the host's static IP and stamps the guest `WATTCP.CFG my_ip = bayIP+100` (netmask `255.255.255.0`, gateway harmless -- air-gapped, no routing; the mesh is all same-subnet L2). 2. **TeslaConsole** -- a per-title **"runs in DOSBox" flag**. When set, apply `+100` at the single point where the console resolves a bay to its game endpoint, so BOTH inherit it: - the **roster entries written into the egg** (peers dial `+100`), AND - the **address the console itself dials** to deliver the egg / poll StateQuery / send StopMission (`bayIP+100:1501`, not the bay IP -- else the SYN hits the launcher, not the DOS game). **Blanket `+100` is valid because every host in a BT/RP egg is itself a DOSBox pod.** Only the BT/RP 4.10 engine is emulated, and ALL of its roles -- cockpit, camera ship, mission review -- are that same one DOSBoxed binary; every other catalog title runs native. So a BT/RP mission roster is entirely emulated and takes `+100` uniformly. The console's own address is never a roster entry (pods reply on the console's inbound socket), so it's never offset. ### What this retires (dev-only artifacts, gone in deployment) `SendToRxAdapters`, the Windows Network Bridge, the two-TAP setup, and `tap2_mirror.py` all existed ONLY because dev co-located the console on the pod PC and had to loop pcap frames back to the host stack. On the real air-gapped segment the console is a separate machine and hears every pod over the wire. Also gone: the machine-specific `realnic=DB5521D` GUID (postinstall binds the one NIC) and the `200.0.0.x` hardcoding (postinstall stamps the assigned IPs). **Co-located smoke-test caveats (all hit 2026-07-10, first dist smoke test; end-to-end egg->mission achieved same night):** running TeslaConsole on the pod PC itself brings dev-era requirements back. On the real segment, with the console on its own machine, none of these apply: 1. Npcap `SendToRxAdapters` must list the pod NIC's `\Device\{GUID}` (restart the npcap service after setting it -- the stop fails silently while DOSBox holds a capture). 2. The host must NOT hold the game IP. A leftover static at bayIP+100 (the old bridge address) makes the console dial itself -- source = destination, the SYN never reaches the wire, `SynSent` forever. 3. **Disable NIC checksum offload (+ LSO) on the pod NIC** (`Disable-NetAdapterChecksumOffload`/`-NetAdapterLso`). The host's OWN outbound frames are captured BEFORE the hardware fills in IP/TCP checksums; WATTCP silently discards every bad-checksum IP packet while still answering checksum-less ARP -- so ARP resolves, the NIC accepts the frames, and the pod stays dead silent at the TCP layer (netnub prints "discarding..."). This was the final console-connect blocker. Frames arriving over the real wire always carry completed checksums, so separate-machine deployments are immune. Also learned same night, not co-location-specific: **bt/rp REQUIRE sound** (`--no-sound` strips the AWE32; the FAST SOS clock never ticks and BTL4OPT freezes in the RIO-reset busy-wait), and **the pod requires a packaged renderer** (pod-launch refuses to start without one unless `--no-bridge`; the smoke test ran with the dev tree's render bridge via `--root`) -- closes the "freeze renderer vs bundle Python" OPEN item in favor of: MUST ship one. Related guard: `configure.ps1` refuses to guess when several candidate bay IPs survive its physical-adapter filter (`-BayIp x.x.x.x` disambiguates, and also allows deliberately binding a virtual adapter on a dev rig); `-ConsoleIp` sets the WATTCP gateway/nameserver (default: the bay IP). > **STATUS 2026-07-11: the complete archive is INSTALL-VERIFIED.** The final > `TeslaPod410.zip` (renderer bundled, ~212 MB) was extracted fresh to > `C:\Games`, configured, and launched end-to-end on the co-located test rig > -- works as expected. Remaining external per-cockpit step: the one-time > manual Npcap install (+ the co-located caveats below when the console > shares the machine). ## The archive (self-contained -- air-gap forbids any download) Single root folder + `postinstall.bat`. Everything the install needs is inside: - `dosbox-x.exe` -- a **static build** (182 MB; imports only Windows system DLLs, empty delay-import table), so **no MinGW/SDL/VC++ DLLs to bundle**; the only runtime DLL, `wpcap.dll`, comes from the Npcap install - `ALPHA_1\` game content (REL410\BT, REL410\RP, SB16, GAUGE, ...) + `net-boot\` drivers (LSL/NE2000/ODIPKT/NET.CFG @ PORT 340/INT 10, loop.bat) - ~~Npcap silent installer (bundled)~~ **retired 2026-07-10 (operator consensus): Npcap is installed MANUALLY, once per cockpit PC** -- a one-time affair that survives game updates. postinstall detects an existing install and only warns when absent (a bundled `deploy\npcap.exe` is still honored if shipped) - The renderer -- **RESOLVED 2026-07-10: frozen to one exe** (PyInstaller onefile, `render-bridge\freeze.ps1` -> `dist\renderer.exe`, ~33 MB, GL backend live-verified from a pure-dist launch). `package.ps1` bundles it automatically when present. - `net_*.conf` **templates** with `@@ROOT@@` / `@@REALNIC@@` tokens, launch dispatcher, `postinstall.bat` ## `postinstall.bat` responsibilities (elevated, one-shot, no network fetch) Implemented in `deploy/` (`postinstall.bat` = thin wrapper; `configure.ps1` does 2-4): 1. **Check Npcap** (manual-install policy, 2026-07-10): detect the existing service and move on; warn clearly if absent. Silent `/S` install only if a bundled `deploy\npcap.exe` was shipped. 2. **Bind the NIC**: pick the one active adapter, write a safe letter-leading `realnic=` fragment (a contiguous substring of ONE GUID block, so it matches `\Device\NPF_{GUID}`; avoids the leading-digit-as-index trap). 3. **Stamp game identity**: `WATTCP.CFG my_ip = + 100` in every `ALPHA_1\...\WATTCP.CFG`, plus a stable `macaddr = 02:00:`. 4. **Relocate paths**: render `net_*.conf.tmpl` -> `net_*.conf`, filling `@@ROOT@@` (the package dir), `@@REALNIC@@`, `@@MACADDR@@`. NOT done here: registering the launch entry-point (console does that), firewall rules, SendToRx, TAP, or mirror. ## Launch dispatch TeslaConsole invokes a **single entry-point** in the game dir with args selecting game/mode. The dispatcher maps args -> conf + renderer: `bt`/looped -> `net_loop.conf`; `rp` -> `net_rp.conf`; the "alternate games/ modes" map onto the decoded `ExitCodeID` set (RunBattleTech/RunRedPlanet + SinglePlayer variants, RunCamera, RunMissionReview, test patterns, TestPlasmaDisplay, ResetRIO). **The entry-point MUST be a supervising parent, not fire-and-forget** -- it stays authoritative for the whole session, lifetime == the game's: - It creates a **Windows Job Object** with `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`, launches DOSBox-X **and** the render bridge *into* the job, then blocks on DOSBox (also doing the window focus/layout, folding in `focus_dosbox.ps1`). - **Kill the entry-point -> DOSBox + renderer die with it.** The kernel enforces the teardown when the job's last handle closes (the instant the entry-point process dies), so it holds even on a hard `TerminateProcess` -- which is exactly the hung-DOSBox case, where no cooperative shutdown can be relied on. No orphaned emulator/renderer, ever. - When DOSBox exits on its own (mission end), the supervisor tears down the job (renderer too) and returns DOSBox's exit code. Ship the entry-point as a small **compiled supervisor exe** (C#/C++) that creates the job, `CreateProcess`es DOSBox + the bridge *into* it, and blocks on `WaitForSingleObject(dosbox)`. It **must NOT be a batch file / `cmd.exe`, nor use `start` or `Start-Process`** -- all of those DECOUPLE from the child: a `start`ed / `Start-Process`ed DOSBox is an independent process, and even a foreground `dosbox.exe` under `cmd.exe` is NOT killed when cmd is (Windows doesn't reap children with the parent gone, absent a job object). The current `launch_pod.ps1`/`pod_deploy.ps1` do exactly this wrong (`Start-Process` + pid-files, then exit, leaving DOSBox running) and must be replaced by the supervisor. **Kill flow:** console -> TeslaLauncher terminates the entry-point process -> job closes -> DOSBox + renderer die. Graceful mission-end still flows to the game as StopMission on `:1501`, after which the supervisor exits with DOSBox. ## Provisioning rules - Bays static in `.1-.32`, game endpoints auto-fall in `.101-.132`, console + its special hosts elsewhere in the /24. All on one flat, air-gapped L2 segment. - Dumb unmanaged switch; no venue cooperation needed. ## OPEN items 1. Per-rig display RECTS (already isolated in `pod_deploy.ps1`; deployment variant of the window layout). (Renderer packaging was OPEN here; RESOLVED 2026-07-10 -- frozen renderer.exe, see the archive section. An XP/Win11 fleet-floor port is planned but BACK-BURNERED: `XP-PORT-PLAN.md`.) Related: `NET-NOTES.md`, `LAUNCH.md`, `ENV-VARS.md`, memory `pod-multiplayer-mesh`, `pod-deployment`.