← Default view
Drawing ATL-OVL-01 Windows ⇄ Linux overlap Issued 2026-07-30 Rev. C

Living in
both OSes

General arrangement for running one developer workstation across two installs for months without the setup rotting.

Scale1 asset : 1 conduit
Conduits12 + 1 omitted
Abandoned routes5
References37
Depthsurvey
Sheet1 of 1
General note — read before building

Every asset class gets exactly one source of truth and a replicator that understands the OS difference. One canonical copy in a neutral store; each OS materialises it locally; edits go back to the store, never sideways between installs. Never a shared partition holding a working tree [10], never a cloud-sync folder holding a .git directory [16].

Key

Legend

▭ TerminalHost-local materialisation. Editable, disposable, rebuildable.
◻ JunctionThe neutral store. The only editable truth.
⌁ ReplicatorThe tool that knows the OS difference.
⊗ SeveredRoute drawn only so it is not built by accident.
⚠ CautionWorks, with a documented trap on one side.
PowerShell mark

Host A · Windows

Incumbent. Owns Outlook, Teams desktop, DPAPI, Credential Manager, NTFS.

A Linux desktop session

Host B · Linux

Candidate. Owns ext4, case sensitivity, the exec bit, LF, a real kernel.

Plan 1

Conduit schedule

One row per asset class

Read each row left-to-right: what exists on Windows, what the single source of truth is, what replicates it, what exists on Linux. The junction is the only box you edit.

C-01

Code & repositories

Spec'd
Host A terminal

C:\src\<repo> — native NTFS, cloned locally

Source of truth
Git remote (GitHub, Gitea)
⌁ Replicatorgit clone natively on each OS, into a native filesystem
Host B terminal

~/src/<repo> — ext4, cloned locally

NoteTwo working trees, one remote. This is the boring answer and it is correct. Under WSL2 the same rule holds for the same reason — the tree lives on the Linux side, because crossing /mnt/c is 2–10× slower and git, which stats thousands of files, is the worst-affected workload [36][37].
C-02

Repo-level behaviour

Spec'd
Host A terminal

CRLF checkout where the repo says so

Source of truth
.gitattributes, committed
⌁ ReplicatorGit itself enforces it identically on both installs
Host B terminal

LF checkout where the repo says so

Note* text=auto plus explicit rules for binaries and shell scripts; run git add --renormalize . once after adding it. Per-machine core.autocrlf cannot be enforced across machines and is now regarded as the cause rather than the cure [14]. Leave core.fileMode at its default true — you only disable it to paper over a filesystem that cannot represent the bit [9].
C-03

Dotfiles

Spec'd ⚠
Host A terminal

%USERPROFILE% targets, Windows paths, Windows Terminal

Source of truth
chezmoi source repo (private git) ⭐ 21k
⌁ Replicatorchezmoi apply, one templated file branching on .chezmoi.os [2]
Host B terminal

$HOME targets, XDG paths, the Linux terminal

NoteWhen a config genuinely must differ per OS it is not two files — it is one templated file with a per-OS branch. On Windows, symlink creation needs developer mode or SeCreateSymbolicLinkPrivilege, and elevated setup scripts need Start-Process -Wait or chezmoi races ahead [2]. .chezmoi.kernel.osrelease separates WSL from bare Linux if you end up with three environments rather than two [2].
C-04

Machine-local git config

Spec'd
Host A terminal

Rendered ~/.gitconfig + per-context includes

Source of truth
One ~/.gitconfig template with includeIf
⌁ Replicatorchezmoi writes the includes; git resolves them per directory [9]
Host B terminal

Rendered ~/.gitconfig + per-context includes

NoteincludeIf "gitdir:~/work/" makes work vs personal signing keys and emails follow the directory, on both OSes, from a single file [9]. No duplication, no per-machine drift.
C-05

Secrets & passwords

Spec'd
Host A terminal

Vault CLI + agent. DPAPI blobs stay where they are — they do not travel.

Source of truth
Password-manager vault (1Password, Bitwarden, pass)
⌁ ReplicatorCLI + agent on both OSes; chezmoi reads secrets at apply time [3]
Host B terminal

Vault CLI + agent. Re-authenticate; never export.

NoteThe vault is the only truly OS-neutral secret store — the OS keyring is not the portable layer, it is a per-OS cache of it. No secret is ever committed to the dotfiles repo; files that must be committed get age or gpg encryption instead [3].
C-06

SSH authentication keys

One key per install
Host A terminal

Key A — enrolled at the host, attributable to this install

Source of truth
Hardware token, or the vault
⌁ ReplicatorDeliberately none. Two keys, both public halves enrolled [23]
Host B terminal

Key B — enrolled at the host, attributable to this install

NoteThis is the one conduit that is intentionally not a conduit. Copying a private key between installs doubles its exposure and destroys your ability to say which machine leaked [23]. Two shapes make per-install keys cheap: the 1Password SSH agent, which serves keys to git and ssh without the client ever reading the private key [24], or a hardware token.
Exception — detail

An ed25519-sk resident key is physically single, so the same credential legitimately serves both installs: you plug in and pull the handle back down, and the private key never leaves the hardware [22].

A FIDO2 USB security token
Spec

ssh-keygen -O resident, firmware 5.2.3+, FIDO2 PIN set. The same token can sign commits, keeping auth and signing on one device [26].

C-07

Git host credentials

Spec'd ⚠
Host A terminal

GCM backend wincredman (default) or dpapi

Source of truth
The git host itself (GitHub, Azure DevOps)
⌁ ReplicatorGit Credential Manager ⭐ 9.1k, swapping its backing store per OS [18]
Host B terminal

GCM backend secretservice, or gpg/pass

NoteNever migrate DPAPI blobs — re-authenticate on the Linux side. secretservice (libsecret / GNOME Keyring) requires a graphical session, so a headless or SSH-only Linux box falls back to gpg/pass with pinentry configured [18]. If you prefer git's own helper, git-credential-libsecret is the modern Linux choice — the old libgnome-keyring helper has been deprecated since 2014 [19].
C-08

Commit signing

Spec'd
Host A terminal

Same key file path, resolved by the chezmoi template

Source of truth
SSH public key — gpg.format = ssh
⌁ Replicatoruser.signingkey, commit.gpgsign, gpg.ssh.allowedSignersFile [25]
Host B terminal

Same key file path, resolved by the chezmoi template

NoteSupported since git 2.34, and it deletes an entire GPG-agent-per-OS problem from the overlap [25]. A FIDO2 -sk key signs commits too [26].
C-09

Browser state

Spec'd ⚠
Host A terminal

Signed-in browser profile

Source of truth
Browser sync account
⌁ ReplicatorFirefox / Chrome sync, end-to-end encrypted [30]
Host B terminal

Signed-in browser profile

NoteFirefox additionally syncs extension settings and data, which Chrome does not [30]. Do not treat the browser's password store as the source of truth: Chrome and Firefox sync payloads are cryptographically incompatible, so a browser switch later strands you. The browser is a client of the vault at C-05, not a peer of it.
C-10

IDE settings

Spec'd
Host A terminal

Windows paths, Windows terminal profiles — machine-scoped, and deliberately local

Source of truth
⌁ ReplicatorAccount sign-in on both installs [27][28]
Host B terminal

Linux paths, Linux terminal profiles — machine-scoped, and deliberately local

NoteSettings, keybindings, snippets, tasks, UI state, extensions and profiles travel; machine and machine-overridable scoped settings are excluded by design — which is exactly what you want for paths and terminal profiles. Keybindings sync per platform by default; toggle settingsSync.keybindingsPerPlatform to unify them [27]. Rider's plugin is bundled and on by default, covering themes, keymaps, code styles, live templates and the enabled-plugin list [28].
C-11

Documents & media

Spec'd
Host A terminal

Native sync client

Source of truth
Cloud sync (Dropbox, Syncthing)
⌁ ReplicatorNative client on both sides
Host B terminal

Native sync client

NoteFine — these are files, not repos. The distinction is the whole point: a sync folder is a correct home for documents and a catastrophic one for a .git directory (see route R-B).
C-12

Toolchain versions

Spec'd
Host A terminal

SDK installed per machine, version selected by the repo

Source of truth
global.json, committed in the repo
⌁ ReplicatorThe .NET CLI resolves it on both OSes; rollForward: latestFeature [33]
Host B terminal

SDK installed per machine, version selected by the repo

NoteThe single highest-leverage file on this sheet: it pins which SDK the CLI uses and accepts later patch/feature bands without per-machine edits. .NET 10 adds a paths field — [".dotnet", "$host$"] — that lets a repo carry its own SDK and stop caring what the host has installed [33].
C-13

App config — Outlook, Teams

No conduit · accept the loss
Host A terminal

Outlook profile, Teams desktop client

Does not travel
Host B terminal

Email and calendar happen in a browser

NoteThe classic Teams desktop client was retired on Linux in favour of a progressive web app [29], and there is no new-Outlook desktop client for Linux. Budget this as a known cost of the overlap rather than a problem to solve — the failure mode here is spending a week fighting it.
Plan 2

Abandoned routes

Drawn so they are not built
Do not build
R-A

Shared NTFS / exFAT partition holding a working tree

Severed
Host A

Windows SIDs, no exec bit, case-insensitive

Route severed Filesystem semantics do not reconcile
Host B

uid/gid, exec bit, case-sensitive, symlinks

  • It fails at step one. git init on an NTFS mount can fail outright: chmod on …/.git/config.lock failed: Operation not permitted [11].
  • Phantom modifications. The executable bit is not preserved, so git reports changes until you set core.fileMode false — disabling a real signal to work around the medium [10][9].
  • Silent case collapse. A repo containing File.txt and file.txt loses one of them on the Windows side; folders differing only by case get merged [15].
  • Symlinks degrade to text. Without SeCreateSymbolicLinkPrivilege or developer mode, git stores them as plain files [9].
  • Ownership never matches. Windows SIDs against Linux uid/gid, papered over with uid=/gid=/dmask/fmask mount options [10].
  • The foundation itself is unsettled in 2026. Field reports on in-kernel ntfs3 include out-of-memory on large copies, hangs and data loss, with the practical advice being to blacklist it and take the slower, reliable ntfs-3g [13]. In January 2026 an LKML "ntfs filesystem remake" series argued ntfs3 "still has many problems and is poorly maintained", offering a replacement passing 308 xfstests against ntfs3's 235 [12].
Elevation

A shared partition looks like it solves drift — one copy, two OSes. It only moves the divergence into filesystem semantics you cannot reconcile.

A bridge collapsed into the river
Verdict

Both ends still stand. The span does not. Clone twice instead.

R-B

Cloud-sync folder holding a .git directory

Severed
  • Incompatible atomicity assumptions. File sync and transactional VCS disagree about what a write is; a badly timed sync cycle over git's internal files corrupts the repo, and the community verdict is that corruption is a matter of when, not if [16][17].
  • The supported shape, if you want it. git-remote-dropbox uses the Dropbox API rather than the desktop client's file operations — Dropbox as a remote, not as a working tree [17].
  • What the sync folder is genuinely for. Research notes, documents, media — see C-11. The rule is about .git, not about Dropbox.
R-C

Repository under /mnt/c in WSL2

Severed
  • Cross-boundary file access runs roughly 2× to 10× slower, and git is disproportionately affected because it stats many files [36]. The upstream issue is long-running and well known [37] ⭐ 33k.
  • Same rule as R-A, different mechanism: the working tree belongs on a native filesystem owned by one OS.
R-D

Shared package cache across OSes

Severed
  • NuGet defaults to %userprofile%\.nuget\packages and ~/.nuget/packages; NUGET_PACKAGES can point both at one path [32] — and doing so buys nothing while risking path-encoded breakage.
  • Caches (NuGet, npm, bun) are derived state. They are deliberately not an asset class on this sheet. Let each install rebuild its own.
R-E

Hand-copying dotnet user-secrets between installs

Severed
  • User secrets live at %APPDATA%\Microsoft\UserSecrets\<id>\secrets.json and ~/.microsoft/usersecrets/<id>/secrets.json — per-user, per-OS, outside the project. The ID in the .csproj is shared; the file is not [31].
  • Treat the vault at C-05 as the source and script the re-seed. Copying JSON by hand creates a second editable copy, which is the one thing this whole sheet exists to prevent.
Detail A

Material selection — dotfile manager

Conduit C-03

Three bars must be cleared at once: real Windows support, per-OS templating from a single branch, and secrets pulled live from a vault rather than committed. Exactly one option clears all three.

Material⭐ StarsWindowsPer-OS templatingSecretsLearning cost
chezmoi
Specified
⭐ 21k [1] ✓ Go templates, .chezmoi.os [2] ✓ Vault integration + age/gpg [3] Medium
yadm ⭐ 6.4k [1] ~ ##os.Linux alternate files only [8] Encryption yes, vault [1] Low
GNU Stow not on GitHub ✗ needs symlinks + Perl [4] Very low
Bare git repo [1] [1] Low
home-manager ⭐ 10k ✗ native; WSL only [5] ✓ (sops-nix) Very high

Counter-view on record

On Hacker News, developers who evaluated Stow and trialled chezmoi report settling on yadm for its lower ceremony [7]. Worth knowing — but the friction people hit with yadm is precisely this migration's case: identical content that must live at different paths on different OSes [8]. That is chezmoi's home turf.

Why not Nix

home-manager is the technically strongest answer for Linux alone and has no native Windows story — it reaches Windows only through WSL, where Windows programs cannot even follow the symlinks it creates, and git and git bash on Windows emit errors [5][6]. Right tool, wrong period.

Detail B

Shell parity — known deviations

Tolerances
The PowerShell mark
Detail B · PowerShell 7 on both hosts

Specification

Keep PowerShell as the scripting language on both hosts. One templated profile.ps1 in chezmoi: the paths differ (~/.config/powershell/profile.ps1 on Linux, Documents\PowerShell on Windows) but the content is one file [20][21].

Do not fight to make it your Linux interactive shell — the ecosystem there (completions, prompts, pipelines, man pages) is bash/zsh/fish-shaped. Put shared logic in .ps1 files invoked from either shell; keep interactive aliases in a chezmoi-templated shell rc.

Terminal emulator: pick a cross-platform one (WezTerm, Alacritty) so keybindings are a single config file rather than Windows Terminal JSON on one side and something else on the other.

What breaks on the Linux host [20]

  • No COM. New-Object -ComObject fails.
  • No Get-WinEvent, no Get-CimInstance, no ActiveDirectory module. Windows-specific modules are simply not shipped.
  • Paths become case-sensitive. Get-Item "Report.csv" fails against report.csv — the same class of surprise as the case collapse in route R-A, arriving from the other direction.
  • .NET Framework-targeted modules will not load. Only .NET (Core) builds.
  • Remoting over WinRM needs NTLM/Negotiate or Basic-over-HTTPS. No Kerberos.
Detail C

.NET & TS toolchain schedule

Conduit C-12
ConcernHost A · WindowsHost B · LinuxAlignment method
SDK version Per-machine install Per-machine install global.json in the repo, rollForward: latestFeature [33]
User secrets %APPDATA%\Microsoft\UserSecrets\<id>\secrets.json [31] ~/.microsoft/usersecrets/<id>/secrets.json [31] ⚠ Not synced by design — re-seed from the vault
NuGet cache %userprofile%\.nuget\packages [32] ~/.nuget/packages [32] ✗ Do not share — it is a cache; let each rebuild it
Scheme B

Alternate scheme — move the toolchain into the container

For a long overlap
Recommended if the overlap runs long
A yard of stacked shipping containers
Scheme B · one environment, described as code

What it is

devcontainer.json is an open specification for describing a development environment — OS, runtime, tools, extensions, services — as code, supported by a range of tools rather than one vendor [34]. Microsoft publishes .NET images and documents dev containers specifically as the way to try SDK versions without touching the host install [35].

What it buys

  • The SDK, runtime and tool set stop being per-OS state — they are in the repo, so there is nothing to keep aligned between installs [34].
  • Testing a new .NET version perturbs neither host [35].
  • The container is Linux either way — so your build runs on Linux semantics (case-sensitive, LF, exec bit) from day one on Windows too. You discover porting problems before you switch, not after.

What it does not buy

  • It covers none of C-03 through C-11: dotfiles, shell, credentials, browser, IDE settings. Every other row of the schedule still applies.
  • On Windows it runs over WSL2, so route R-C reappears — the repo belongs on the Linux side of the boundary [36].
Maint.

Standing orders

Anti-drift, daily
  1. Both installs run chezmoi apply on login. Drift then shows up as a diff, not as a surprise months later [1].
  2. Run chezmoi diff before any config edit, so you never edit the materialised file instead of the source [1].
  3. Nothing is a source of truth unless it is in a git remote, a vault, or a sync account. If it only exists on one install, it does not exist.
  4. Every unpushed branch is drift. Push daily, even work in progress.
Rack-mounted servers
The neutral store — remote, vault, sync account
A solid state drive
The terminal — a native filesystem owned by exactly one OS
Index

Related sheets

Refs

Reference documents

37 sources
Drawing title Living in both OSes — general arrangement Windows ⇄ Linux migration overlap · one source of truth per asset class
Drawing no.ATL-OVL-01 Sheet1 of 1
Issued2026-07-30 Rev.C — routes R-A…R-E severed