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.
Host A · Windows
Incumbent. Owns Outlook, Teams desktop, DPAPI, Credential Manager, NTFS.
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 terminalC:\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 terminalCRLF checkout where the repo says so
Source of truth
.gitattributes, committed
⌁ ReplicatorGit itself enforces it identically on both installs
Host B terminalLF 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 terminalRendered ~/.gitconfig + per-context includes
Source of truth
One ~/.gitconfig template with includeIf
⌁ Replicatorchezmoi writes the includes; git resolves them per directory
[9]
Host B terminalRendered ~/.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 terminalVault 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 terminalVault 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 terminalKey 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 terminalKey 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 — detailAn 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].
Specssh-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 terminalGCM backend wincredman (default) or dpapi
Source of truth
The git host itself (GitHub, Azure DevOps)
Host B terminalGCM 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 terminalSame 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 terminalSame 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 terminalSigned-in browser profile
Source of truth
Browser sync account
⌁ ReplicatorFirefox / Chrome sync, end-to-end encrypted
[30]
Host B terminalSigned-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 terminalWindows paths, Windows terminal profiles — machine-scoped, and deliberately local
Source of truth
⌁ ReplicatorAccount sign-in on both installs
[27][28]
Host B terminalLinux 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 terminalNative sync client
Source of truth
Cloud sync (Dropbox, Syncthing)
⌁ ReplicatorNative client on both sides
Host B terminalNative 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 terminalSDK 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 terminalSDK 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 terminalOutlook profile, Teams desktop client
⊘
Does not travel
Host B terminalEmail 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 AWindows SIDs, no exec bit, case-insensitive
⊗
Route severed
Filesystem semantics do not reconcile
Host Buid/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].
ElevationA shared partition looks like it solves drift — one copy, two OSes. It only moves the divergence into filesystem semantics you cannot reconcile.
VerdictBoth 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 | ⭐ Stars | Windows | Per-OS templating | Secrets | Learning 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
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
| Concern | Host A · Windows | Host B · Linux | Alignment 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
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
- Both installs run
chezmoi apply on login. Drift then shows up as a diff, not as a surprise months later [1].
- Run
chezmoi diff before any config edit, so you never edit the materialised file instead of the source [1].
- 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.
- Every unpushed branch is drift. Push daily, even work in progress.
The neutral store — remote, vault, sync account
The terminal — a native filesystem owned by exactly one OS
Index
Related sheets
This drawing · full textLiving in both OSesThe canonical write-up this sheet is drawn from — 37 citations, 14 minutes.
Parent expeditionTesting the waters: a staged, reversible path from a Windows dev workstation to LinuxClimb WSL2 → live USB → external-SSD install → commit, skip dual-boot entirely.
SurveyThe staged ladder: what each rung provesWhat each rung proves, what it structurally hides, the go/no-go signal, and the cost of retreating.
SurveyWSL2 as rung zero — and the exact wall it hitsRetires every userland risk and none of the machine-ownership risks.
SurveyDual-boot without wrecking the Windows installSecond NVMe, two OSes ignorant of each other, Linux picked from the firmware boot menu.
SurveyDropbox on Linux and shared foldersbtrfs/zfs/xfs now supported — but one folder cannot serve both installs, and repos do not belong there at all.
SurveyTrial hardware and VM pathsLive USB first, then a full install on an external NVMe SSD. Everything else is a detour.
ReconExit criteria and the blocker inventoryTickable go/no-go criteria and a pre-committed rollback date.
ArchiveAtlasAll research sheets.
Refs
Reference documents
37 sources