Docs: bring READMEs in line with current state (net48, JSON wire)

- Root README: launcher is .NET Framework 4.8 (was .NET 6/x86); RPC is framed
  JSON (was BinaryFormatter); added Contract/ + SecureConfig/; dropped the
  now-resolved "known duplication" warning; corrected the lib/*.dll note
  (TeslaConsoleLaunchLib/TeslaSecureConfiguration are source-built, kept only as
  test baselines); added a short history section.
- Console README: Contract is net48-only; Launcher targets net48.
- Launcher README: net48 framework-dependent (was net8/x64 self-contained);
  needs .NET Framework 4.8 (built into Windows), no bundled runtime.
- DiffTests README: original 4.11.3.37076 vs recovered 4.11.4.x (no longer share
  identity); noted version-insensitive comparison + the new protocol/crypto guards.
- Removed Launcher/assets/MEMORY.md (stale leftover dev-notes, superseded by the
  README and now-moot post-BinaryFormatter).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-06-30 13:44:06 -05:00
co-authored by Claude Opus 4.8
parent 2a3d1c1ef6
commit d5c4be26ce
5 changed files with 69 additions and 167 deletions
+9 -9
View File
@@ -39,10 +39,10 @@ The console references these assemblies. Most are vendored as binaries under
Two of these are no longer vendored binaries — they are built from source and
shared across the suite:
- `TeslaConsoleLaunchLib``../Contract/Tesla.Contract.csproj`, a multi-targeted
`net48;net8.0-windows` project: the single source of truth for the Console↔Launcher
RPC contract (wire types, the `PodManagerConnection` client, and the framed-JSON
`PodRpc` protocol), shared with the Launcher Service. The assembly keeps the
- `TeslaConsoleLaunchLib``../Contract/Tesla.Contract.csproj`, a net48 project: the
single source of truth for the Console↔Launcher RPC contract (wire types, the
`PodManagerConnection` client, and the framed-JSON `PodRpc` protocol), shared with the
Launcher Service. The assembly keeps the
`TeslaConsoleLaunchLib` name so the original-exe baseline still resolves in the
differential tests; the wire no longer embeds assembly names (see RPC note below).
- `TeslaSecureConfiguration``../SecureConfig/Tesla.SecureConfig.csproj` (net48),
@@ -58,11 +58,11 @@ decompiled to source the same way if full-source builds are needed.
The pod-management channel (TCP 53290) runs **length-prefixed System.Text.Json**
frames over the existing OFB-encrypted stream — see `Contract/PodRpcProtocol.cs`,
shared verbatim by both ends. This replaced the original `BinaryFormatter` +
serialized-`MethodBase` scheme (a remote-code-execution sink and the reason the
Launcher was pinned to EOL .NET 6); dispatch is now by method-name string. The
Launcher Service/Agent target **net8**. Note the Console still uses `BinaryFormatter`
for *local* disk persistence (`Site` config, mission results) — that is local file
I/O on net48, not the network surface, and is intentionally left alone.
serialized-`MethodBase` scheme (a remote-code-execution sink, and what had pinned the
Launcher to an old runtime); dispatch is now by method-name string. The Launcher
Service/Agent target **net48**, same as the Console. Note the Console still uses
`BinaryFormatter` for *local* disk persistence (`Site` config, mission results) — that
is local file I/O on net48, not the network surface, and is intentionally left alone.
## Layout
+18 -6
View File
@@ -6,12 +6,14 @@ source in this repo) behaves identically to the **original** reference binary in
## How it works
Both files carry the *exact same* assembly identity
(`TeslaConsole, Version=4.11.3.37076`), so the .NET loader will not hold both in
one AppDomain. The suite therefore loads each assembly into its **own child
AppDomain** (`DifferentialFixture`) and drives it through a `MarshalByRefObject`
proxy (`Invoker`). This is why the project targets **net48** — AppDomains are a
.NET Framework feature.
The suite loads each assembly into its **own child AppDomain**
(`DifferentialFixture`) and drives it through a `MarshalByRefObject` proxy
(`Invoker`). This is why the project targets **net48** — AppDomains are a .NET
Framework feature. The **original** is the `4.11.3.37076` baseline; the **recovered**
build is the modernized `4.11.4.x` line (same `TeslaConsole` assembly name, an
intentionally newer version). Because the two versions differ, the public-member
comparison strips `Version=` stamps before diffing — it compares type/member *names*,
not assembly versions.
Each child domain is given a probe directory (the recovered build's output, which
ships every dependency DLL) so the original — which is distributed without its
@@ -39,6 +41,16 @@ proprietary dependencies — still resolves its references for metadata inspecti
A negative-control test (`Harness_Distinguishes_Different_Outputs`) proves the
harness can actually see a difference, so a green run is never vacuous.
The project also carries two **byte-compatibility guards** — not original-vs-recovered
comparisons, but checks that the modernized protocol/crypto stays compatible with the
original binaries:
- `PodRpcProtocolTests` — round-trips the framed-JSON RPC ([`Contract/PodRpcProtocol.cs`](../../../Contract/PodRpcProtocol.cs))
in-process: every request/response shape encodes and decodes back to the same values.
- `SecureConfigCompatTests` — asserts the source-built `OFBCryptoStream` produces
byte-identical ciphertext to the original `TeslaSecureConfiguration.dll`, so the pod
provisioning handshake stays wire-compatible.
## Running
```
+8 -7
View File
@@ -1,6 +1,6 @@
# TeslaLauncher
.NET 8 (win-x64, self-contained) rewrite of the original Elsewhen Studios LLC software (Windows 2000 / .NET Framework 2.0).
.NET Framework 4.8 (framework-dependent) rewrite of the original Elsewhen Studios LLC software (Windows 2000 / .NET Framework 2.0). net48 ships in Windows 10/11, so the pod needs no separate runtime install and the package stays small (~3.7 MB).
## Architecture
@@ -42,9 +42,9 @@ TeslaConsole ──TCP 53290 (OFB + framed JSON)──> TeslaLauncherService
| File | Description |
|------|-------------|
| `TeslaLauncherService.cs` | Windows Service implementation |
| `TeslaLauncherService.csproj` | Service project (net8.0-windows, x64, self-contained) |
| `TeslaLauncherService.csproj` | Service project (net48, generic host + Windows service) |
| `TeslaLauncherAgent.cs` | Userspace Agent implementation |
| `TeslaLauncherAgent.csproj` | Agent project (WinForms, net8.0-windows, x64) |
| `TeslaLauncherAgent.csproj` | Agent project (WinForms, net48) |
| `LaunchModels_Shared.cs` | Service↔Agent IPC types (Tesla.Launcher.Shared). Wire types (Tesla.Net) now come from `../Contract/Tesla.Contract.csproj` |
| `SecureConfig.cs` | First-boot secure configuration protocol |
| `build.bat` | Builds both components |
@@ -53,7 +53,7 @@ TeslaConsole ──TCP 53290 (OFB + framed JSON)──> TeslaLauncherService
## Building
Requirements:
- .NET 8 SDK
- .NET SDK (6.0+) to drive the build
- Internet access for NuGet restore (first build only)
```
@@ -63,9 +63,10 @@ build.bat /agent :: build Agent only
```
Output goes to `TeslaLauncher\` with `Service\` and `Agent\` subdirectories plus
`install.bat`. The projects are published in place (self-contained, single-file,
win-x64) — they reference `../Contract`, so they cannot be staged into a temp
folder. No .NET runtime is required on the target pod.
`install.bat`. The projects are published in place (framework-dependent net48) — they
reference `../Contract`, so they cannot be staged into a temp folder. Each folder holds
the exe plus its dependency DLLs; the target pod needs only .NET Framework 4.8 (built
into Windows 10/11), no bundled runtime.
## Installation
-115
View File
@@ -1,115 +0,0 @@
# TeslaLauncher Project Memory
## Project Overview
Simulation cockpit launcher system (BattleTech/laser-tag venue).
Original: Windows 2000 app by Elsewhen Studios LLC.
Current: Modern .NET 6 rewrite to work around Session 0 isolation (Vista+).
## Architecture: 3 components
1. **TeslaLauncherService** (Session 0 Windows Service) - TCP 53290, handles Console commands, forwards via Named Pipe
2. **TeslaLauncherAgent** (WinForms tray, user desktop session) - Named Pipe server, launches/kills apps
3. **SecureConfig** (PodSecureConfigurator) - First-boot network configuration protocol
## Key Files
- `TeslaLauncherService.cs` + `.csproj` - Windows Service (net6.0-windows, x86, self-contained)
- `TeslaLauncherAgent.cs` + `.csproj` - Userspace Agent (WinForms, net6.0-windows, x86)
- `LaunchModels_Shared.cs` - BinaryFormatter wire types + JSON IPC types
- `SecureConfig.cs` - First-boot UDP beacon + AES configuration protocol
- `LaunchApps.xml` - Game configs (Red Planet 4.11, BattleTech Firestorm at C:\Games\)
- `build.bat` / `install.bat` - Build and install scripts
## Assets (original binaries)
- `assets/Tesla Application Launcher/` — original pod-side binaries (TeslaLauncherService.exe, TeslaSecureConfiguration.dll, etc.)
- `assets/Tesla Console/` — original console-side binaries (TeslaConsole.exe, TeslaSecureConfiguration.dll, etc.)
- `assets/configoflc.pcapng` — pcap of a SUCCESSFUL pod configuration (used to confirm protocol)
- `assets/pcap.pcapng` — pcap of a FAILING configuration (root cause: Console RPLY sent from wrong NIC)
## SecureConfig Protocol — CONFIRMED from binary (TeslaSecureConfiguration.dll)
All values below are verified from the original DLL.
### UDP Config Phase
- Port 53291: pod broadcasts RQST, Console listens
- Port 53292: Console broadcasts RPLY (UDP), pod listens
- RQST format: `RQST`(4 plaintext) + MAC(6) + RequestId(3) = 13 bytes PLAINTEXT
- RPLY format: `RPLY`(4 plaintext) + AES-256-CBC ciphertext(64 bytes) = 68 bytes
- CBC ciphertext = IV(16) + PKCS7-padded plaintext(48)
- AES key = PBKDF2(passphrase, salt, 1000 iter, 32 bytes)
- Salt (32 bytes): 0x17,0xab,0x51,0xd9,0xec,0xd1,0xd4,0x74,0xa9,0x09,0x4a,0x34,0x27,0xfb,0x1f,0xf2,0xde,0xc4,0xf9,0xf1,0xa6,0xd8,0x9e,0xda,0x15,0x11,0x47,0x65,0x32,0xe7,0xe7,0xef
- Passphrase alphabet: "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" (32 chars)
- RequestId length: 3 chars; Passphrase length: 5 chars
- Temp IP: RANDOM in 172.16.0.0/12 range (byte[0]=172, byte[1]=0x1X random, bytes 2-3 random)
- Our fixed 172.16.0.100 is fine (Console doesn't connect back to temp IP)
- Temp mask: 255.240.0.0
### TCP Session Phase (port 53292) — CONFIRMED from DLL decompilation (ilspycmd)
- Console connects TCP to pod's NEW IP:53292 AFTER UDP config
- Both sides call the SAME `NegotiateCryptoStreams` static method:
1. Both write own 16-byte IV (raw), read other's 16-byte IV
2. OFB setup: outStream keyed from OTHER side's IV, inStream keyed from OWN IV
- Pod writes with consoleIv, reads with podIv
- Console writes with podIv, reads with consoleIv
3. Both write raw "CONF" (4 bytes: 0x43,0x4F,0x4E,0x46) over OFB
Both read 4 bytes and verify == "CONF" → key verification handshake
If mismatch → returns false → caller retries with new TCP connection
- After NegotiateCryptoStreams returns true:
4. Pod → Console: BinaryWriter.Write(string) — RSA-2048 public key XML (~417 bytes)
5. Console reads RSA XML, generates AES-256 session key, RSA-encrypts it
6. Console → Pod: BinaryWriter.Write(int len) + BinaryWriter.Write(byte[] ciphertext)
(260 bytes = 4-byte int32 (256) + 256-byte RSA PKCS#1 v1.5 ciphertext)
7. Pod: ReadInt32() + ReadBytes(len) + RSA.Decrypt(enc, Pkcs1) → session key
- Key derivation: PBKDF2(passphrase, salt, 1000 iter, 32 bytes) — same key both directions
- OFBCryptoStream: unidirectional, constructor calls NextBuffer() immediately (IV→KS block 0)
### Registry / netsh
- Hostname: SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName
- netsh: `interface ip set address "{adapter}" static {ip} {mask} {gw}` with ` 1` metric
- DNS: `interface ip set dns "{adapter}" static {dns}`
## BinaryFormatter Cross-Runtime Findings (.NET FW 2.0 → .NET 6)
- **MemberInfoSerializationHolder field names differ**: .NET FW 2.0 uses "Name", .NET 6 uses "MemberName"
- **MethodInfoProxy** intercepts via binder redirect + ISerializable, reads "Name" field
- **IObjectReference fixup fails for struct fields**: InvokeCommand is a struct; fixup doesn't update Function field in boxed value type. Fixed with [ThreadStatic] `LastResolvedName` fallback.
- **Console checks ICMP reachability** before connecting TCP 53290 — Windows Firewall must allow ICMPv4 type 8
- **BindToName** must write "TeslaConsoleLaunchLib" as assembly for Tesla.Net types in serialized responses
- **JsonElement not [Serializable]**: Agent IPC returns JsonElement via System.Text.Json; must convert to proper Tesla.Net types before BinaryFormatter serialization
## Known Bugs Found During Scan
1. ~~**CONFIG_FILE path mismatch**~~: Fixed
2. **IsMachineConfigured in Agent** doesn't filter virtual adapters (Service version does) - may false-trigger on Hyper-V machines
3. **`new Random()` for passphrase/RequestId generation** - should use RandomNumberGenerator (security)
4. **Named pipe has no PipeSecurity ACL** - any local process can connect and issue commands
5. **Volume control silently fails** if nircmd.exe not present - no fallback
6. ~~**`_clientTasks` list**~~: Fixed with _taskLock
## Wire Protocol
- Console → Service: TCP 53290, OFB-encrypted (NegotiateCryptoStreams + session key), BinaryFormatter RPC
- Service → Agent: JSON over Named Pipe "TeslaLauncherIPC" with 4-byte length prefix
- SecureConfig: UDP 53291 (RQST beacon), UDP 53292 (RPLY), TCP 53292 (OFB + RSA key exchange)
### Management Port (53290) — confirmed from decompiled TeslaLauncherService.exe
- Original: `ManagePort = 53290`, `ConfigPort = 53291`
- Every Console connection: NegotiateCryptoStreams(sessionKey) → BinaryFormatter RPC over OFB
- Session key: 32-byte AES key from RSA exchange during SecureConfig, saved to TeslaKeyStore.key
- Key file: `C:\ProgramData\TeslaLauncher\TeslaKeyStore.key` (format: 1-byte length + key bytes)
- Original Console: `PodManagerConnection.Open()``NegotiateCryptoStreams()``BinaryFormatter.Serialize/Deserialize`
## Install Product Protocol (confirmed from old pod pcap)
- Console uses TWO concurrent TCP 53290 connections:
- **Polling connection**: GetInstalledApps, then GetOutOfBandProgress polling (~4/sec), then InstallApp
- **Install connection**: InitiateInstallProduct → Guid response, then 8-byte Int64 LE file size + raw file bytes, then FIN
- Service handles InitiateInstallProduct and GetOutOfBandProgress directly (not forwarded to Agent)
- Progress tracking: receive (0-50%), extract to C:\Games (50-95%), postinstall.bat (96%), complete (100%)
- Zip-slip protection on extraction; postinstall.bat executed with 60s timeout then deleted
- **FIXED**: Console sent file 3x because `InstallProductWorker` loops `for (i=0; i<3; i++)` and only breaks on `PercentComplete == 99`. We were returning 100 → now returns 99.
## Fixes Applied (session 2)
- **Agent splash-to-normal transition**: form.Close() in timer + Main() falls through to AgentApplication
- **Guid format mismatch**: string-first deserialization with TryParse (moot after moving to Service)
- **InitiateInstallProduct**: handled directly in Service, returns Guid, triggers ReceiveInstallFile
- **LaunchApps.xml ACL**: icacls in install.bat grants Users modify access
- **build.bat /q flag**: suppresses pause for non-interactive terminals
- **install.bat**: creates C:\Games with Users modify ACL
## User Preferences
- User writes in informal English, technical background
- Prefers building via build.bat (not direct dotnet publish)
+34 -30
View File
@@ -4,46 +4,50 @@ The Tesla cockpit-pod software, in one repository:
| Folder | What it is | Target |
|--------|------------|--------|
| [`Console/`](Console/) | **TeslaConsole** — the operator console (WinForms) that configures and drives the pods. A faithful decompiled reconstruction of the original `TeslaConsole.exe`, with a differential test suite that pins it to the original baseline. | .NET Framework 4.8 |
| [`Launcher/`](Launcher/) | **TeslaLauncher** — the pod-side Service (Session 0 RPC listener) + Agent (user-session app launcher). A clean .NET 6 rewrite of the original launcher. | .NET 6 (windows, x86) |
| [`Console/`](Console/) | **TeslaConsole** — the operator console (WinForms) that configures and drives the pods. A decompiled reconstruction of the original `TeslaConsole.exe` (now the modernized 4.11.4.x line), with a differential test suite pinning it to the original 4.11.3.37076 baseline. | .NET Framework 4.8 |
| [`Launcher/`](Launcher/) | **TeslaLauncher** — the pod-side Service (Session 0 RPC listener) + Agent (user-session app launcher). A clean rewrite of the original launcher. | .NET Framework 4.8 |
| [`Contract/`](Contract/) | **Tesla.Contract** — the shared Console↔Launcher RPC contract: wire types, the client, and the framed-JSON protocol. Emits assembly `TeslaConsoleLaunchLib`. | .NET Framework 4.8 |
| [`SecureConfig/`](SecureConfig/) | **Tesla.SecureConfig** — the first-boot pod provisioning protocol (UDP beacons, OFB crypto, RSA key exchange). Emits assembly `TeslaSecureConfiguration`. | .NET Framework 4.8 |
The console and the launcher talk over TCP 53290 using an OFB-encrypted
BinaryFormatter RPC. They share a wire contract (`Tesla.Net` types +
`ILauncherService`/`IPodManagerConnection` signatures) that **must stay byte-for-byte
compatible**.
The console and launcher talk over **TCP 53290** using **length-prefixed
`System.Text.Json` frames over an OFB-encrypted stream** ([`Contract/PodRpcProtocol.cs`](Contract/PodRpcProtocol.cs)),
dispatched by method name. The wire contract lives in one source project
([`Contract/`](Contract/)) referenced by both sides — a single source of truth, no
duplication or hand-syncing.
> ⚠️ **Known duplication (intentional, for now):** that shared contract currently
> lives in two places — the console consumes it as the compiled
> `Console/lib/TeslaConsoleLaunchLib.dll`, and the launcher hand-replicates the same
> types in `Launcher/LaunchModels_Shared.cs`. They must match exactly. Consolidating
> this into a single multi-targeted `Tesla.Contract` source project is the main
> follow-up this monorepo is meant to enable.
> **Note:** Red Planet game *control* uses a separate protocol (Munga, TCP 1501) via the
> vendored `Munga Net.dll` — not the RPC channel above. The game itself is C++ and lives
> in its own repo (`gitea.mysticmachines.com/VWE/RP411.git`).
## Building
Each side has its own toolchain; the combined solution builds both:
```
dotnet build TeslaSuite.sln -c Release
dotnet test Console/tests/TeslaConsole.DiffTests # differential + protocol + crypto guards
```
Or individually:
```
dotnet build Console/TeslaConsole.csproj -c Release # the console
dotnet test Console/tests/TeslaConsole.DiffTests # differential + catalog tests
dotnet build Launcher/TeslaLauncher.sln -c Release # service + agent
```
The launcher also has its original `Launcher/build.bat` / `Launcher/install.bat`
for producing and deploying the self-contained pod build.
**Pod deployment:** [`Launcher/build.bat`](Launcher/build.bat) publishes the
framework-dependent net48 package into `Launcher/TeslaLauncher/` (~3.7 MB on disk /
~1.6 MB zipped — no runtime to install, since .NET Framework 4.8 ships in Windows 10/11),
and [`Launcher/install.bat`](Launcher/install.bat) deploys it on a cockpit PC (registers
the Service, sets up the Agent for auto-login, hardens the box).
## Layout notes
- `Console/original/TeslaConsole.exe` is the reference baseline the differential
tests compare against — keep it.
- `Console/lib/*.dll` are the proprietary binary dependencies the console still
references (`TeslaConsoleLaunchLib`, `TeslaSecureConfiguration`, `Munga Net`,
`BitmapLibrary`, `WeifenLuo` docking).
- `Console/RedPlanet/Apps.xml` is the data-driven product catalog (see the console's
- `Console/original/TeslaConsole.exe` the **4.11.3.37076** reference baseline the
differential tests compare against. Keep it.
- `Console/lib/*.dll` the remaining vendored binary dependencies (`Munga Net`,
`BitmapLibrary`, `WeifenLuo` docking). The original `TeslaConsoleLaunchLib.dll` and
`TeslaSecureConfiguration.dll` are also kept here, but **only as byte-compatibility test
baselines** — both are now built from source (`Contract/`, `SecureConfig/`).
- `Console/RedPlanet/Apps.xml` — the data-driven product catalog (see the console's
Site Management → Add Product / Register Product on Pods).
## History
The system was modernized in 2026: the duplicated wire contract was extracted to a single
source project, `BinaryFormatter` (an RCE sink, and what pinned the launcher to an old
runtime) was replaced with the framed-JSON protocol, and the launcher was rebuilt — briefly
on net8/x64, then settled on net48 to match the console and ship a tiny, runtime-free
package. The whole console↔pod path (provisioning, install, launch) is validated on real
pods.