# The WSL backend lets Windows users run their projects on Linux without leaving the app
*pingdotgg/t3code@main · informative · 8 markers*

> **Default.** The desktop app runs one backend — the Windows-native Node server that the pool registers as its primary — and the WSL backend is off.
>
> **On enable.** Picking a distro in the Connections WSL backend control persists the choice and starts a second pool instance that runs the same server inside that distro through `wsl.exe`, reachable only on loopback and keeping its own Linux home directory.
>
> **wsl-only.** The `wslOnly` toggle instead makes the WSL backend the pool's sole primary and stops the Windows one, but the primary is chosen once at startup so changing it relaunches the app.

**Author:** SquallLeonhart13 · **Updated:** 2026-06-28T17:38:10.666Z

## Markers
1. **Choose the WSL backend mode** — `apps/web/src/components/settings/ConnectionsSettings.tsx:3101–3119`
   The Connections `SettingsRow` titled "WSL backend" renders a `Select` whose options are `Off` plus the distros in `desktopWslState.distros`. Changing it calls `handleSelectWslMode(value)`. This is the only UI that turns the parallel WSL backend on or off; a separate "WSL only" row appears below when `desktopWslState.enabled` is true.
2. **Route the choice to an IPC call** — `apps/web/src/components/settings/ConnectionsSettings.tsx:2941–2963`
   `handleSelectWslMode` maps the picked value to a desktop-bridge call: `Off` → `setWslBackendEnabled(false)`, a distro while disabled → an enable modal that asks parallel-vs-wsl-only, and a distro while already enabled → `setWslDistro(nextDistro)` (a distro swap). Consequential changes — losing a running WSL instance, or any wsl-only change — stage a confirmation through `setPendingWslChange` first.
3. **Persist the setting, then reconcile or relaunch** — `apps/desktop/src/ipc/methods/wsl.ts:59–77`
   `setWslBackendEnabled` persists the flag (or `applyWslWindowsFallback` when disabling) and then runs `wslBackend.reconcile`. When the change flips which backend is primary in wsl-only mode (`changedWslOnlyPrimary`), it calls `lifecycle.relaunch` instead, because the pool's primary spec is fixed at layer init. The sibling `setWslDistro` follows the same shape — reconcile for a secondary swap, relaunch when wsl-only owns the primary.
4. **The three WSL settings** — `apps/desktop/src/settings/DesktopAppSettings.ts:28–42`
   Three persisted flags drive everything: `wslBackendEnabled` (run the WSL backend at all), `wslDistro` (`null` tracks the system default, otherwise a named distro), and `wslOnly` (run only the WSL backend as primary, no Windows process). The legacy `wslMode: "wsl"` swap value is migrated to `wslBackendEnabled: true` on load. `wslOnly` changes need a restart since the primary is chosen once at layer init.
5. **Bring the pool in line with the settings** — `apps/desktop/src/wsl/DesktopWslBackend.ts:189–203`
   `reconcileBody` computes `shouldRun = wslBackendEnabled && available && !wslOnly` — wsl-only is excluded here because in that mode the WSL backend is the primary, not a secondary. It compares the desired `wsl:<distro>` id against the currently-registered WSL instance and starts, stops, or swaps as needed. The whole effect is serialized by a mutex and never fails; errors are logged.
6. **Register the backend in the pool** — `apps/desktop/src/backend/DesktopBackendPool.ts:276–285`
   The pool registers the Windows primary at startup, wiring `configResolve` to `resolvePrimary`; the primary lives for the pool's lifetime and refuses `unregister`. `register(spec)` / `unregister(id)` (lines 313, 364) add and remove backends at runtime, each in its own child scope — `DesktopWslBackend.startNew` calls `pool.register` with `configResolve: configuration.resolveWsl(...)` to bring up the WSL instance on a freshly scanned loopback port.
7. **Launch the server inside the distro** — `apps/desktop/src/backend/DesktopBackendConfiguration.ts:485–503`
   `resolveWslStartConfig` builds a start config that runs `wsl.exe` (optionally `-d <distro>`) and execs `node` on the same server bundle. It omits `t3Home` and strips `T3CODE_HOME` from the forwarded env so the Linux backend uses its own home dir instead of sharing the Windows `~/.t3` SQLite file over `/mnt/c`, and binds `0.0.0.0` inside WSL so Windows can reach it via either localhost forwarding or the distro IP. A preflight checks `node`, its version, and node-pty before the real launch.
8. **wsl-only makes WSL the primary** — `apps/desktop/src/backend/DesktopBackendConfiguration.ts:649–661`
   `resolvePrimary` is the pool's primary spec: when `wslOnly` is set and WSL is available it returns `buildWslPrimaryConfig`, so the single primary backend is the WSL one and no Windows process starts. When wsl-only was requested but WSL is unavailable it logs and falls back to the Windows primary. This decision runs once at layer init, which is why toggling `wslOnly` relaunches the app.

---
Interactive view: https://app.principal-ade.com/trail/9692303b-0e4a-410a-94da-d57b62a1867f
JSON: https://app.principal-ade.com/api/trails/by-id/9692303b-0e4a-410a-94da-d57b62a1867f
Authored at `a9b1190a1` (main).
To open a trail or tour locally in the interactive viewer, see https://app.principal-ade.com for the Principal CLI quickstart.
