Windows support and troubleshooting
August 23, 2026 · View on GitHub
ADE supports the packaged Windows 10/11 x64 desktop, shipped as a public beta. Windows ARM64, native Windows OS computer use, and iOS Simulator remain out of scope. App Control over CDP, the built-in Browser, proof-file ingestion, phone pairing, the local Windows brain, and Windows as an SSH-bootstrap runtime target are supported.
Public Windows installers ship. v1.2.52 was the first signed public release;
downloads are served from https://ade-app.dev/download/windows and the
PowerShell installer from https://ade-app.dev/install.ps1. The exact-SHA proof
in windows-release-proof.md remains a standing
regression check rather than a precondition.
What Windows gets instead
Every capability below behaves differently on Windows than on macOS. The list is exhaustive by intent: if a Windows user notices something missing or weaker and it is not named here, treat that as a bug, not a known gap.
Three outcomes are possible, and they are not interchangeable. Degraded means the capability works with a stated cost. Blocked means the capability is absent and the surface says so. Unavailable means the OS offers no route at all and the surface does not exist.
| Capability | Outcome | What Windows does | Why |
|---|---|---|---|
| Native OS computer use — screenshot, video, GUI automation | Blocked | App Control over CDP, the built-in Browser, and proof-file ingestion all work. Only OS-level capture is gated. | Backed by screencapture and osascript. The Windows equivalent is Windows.Graphics.Capture plus UI Automation, which is a separate project. |
| iOS Simulator drawer, Xcode Preview | Unavailable | Hidden. | Requires macOS and Xcode. |
| Native Notch | Unavailable | Hidden. | macOS window-server feature with no counterpart. |
| Claude Code background-job reattach | Degraded | Each follow-up prompt respawns the CLI instead of replying into the live background job. Turns are slower and in-flight context is lost. | Claude Code ships no control.sock on Windows, and os.userInfo().uid is -1, so there is nothing to attach to. |
| Graceful brain shutdown | Degraded | ADE asks the brain to shut down over its own RPC channel and force-terminates only after the grace window. | Windows has no deliverable SIGTERM; process.kill is TerminateProcess, which no handler can intercept. |
| Orphan agent recovery | Degraded | Recovery uses the on-disk PID registry and a listening-port scan. | Windows cannot read another process's environment, so the macOS ps -wwE environment-tag pass has no equivalent. The port half is implemented. |
| Open files of another process (import-list liveness / CLI session capture) | Degraded | When Sysinternals handle.exe is on PATH, ADE lists files a provider CLI has open and treats that as live. Without it, ADE cannot enumerate handles: it does not guess from mtime except as an explicit last-resort fallback, and Claude --session-id assignment plus transcript capture still identify ADE-owned sessions. | Windows has no lsof. openfiles /query requires a global audit flag. |
| Claude Code sandbox permission modes | Blocked | Permission modes themselves work; the sandbox does not. | Vendor limitation — Claude Code sandboxing is WSL-2 only. Not an ADE gap. |
| Credential storage at rest | Degraded | DPAPI through Electron safeStorage. | No Keychain. DPAPI is user-scoped rather than per-item ACL'd, so it is a weaker boundary than a Keychain item. |
| Remote runtime bootstrap from a locally built channel | Blocked | A local package:beta build sets ADE_RUNTIME_RESOURCES_ALLOW_HOST_ONLY=1 and ships no Darwin/Linux sidecars, so it cannot drive a remote macOS or Linux runtime. CI-built installers are unaffected. | Darwin binaries cannot be produced on a Windows host. |
| Windows as an SSH-bootstrap runtime target | Supported | Windows 10 22H2 (build 19045) and Windows 11 x64 can be bootstrapped over Windows OpenSSH Server. | Requires PowerShell 5.1 or newer and the built-in tar.exe. WSL, ARM64, and Windows Server remain unsupported as targets. |
| Windows ARM64 | Unavailable | x64 only. The Cursor provider is additionally gated on ARM64 even under emulation. | Native and provider payloads are incomplete; @cursor/sdk publishes no win32-arm64 package. |
| Cross-channel phone-sync launch gate | Degraded | The launch gate answers from the sync-host lock file alone. A brain that was hard-killed and left a stale lock is not detected, so two channels can briefly both hold phone sync. ade serve still runs the full scan when it starts a brain. | The listener scan is a full-machine Get-NetTCPConnection + Get-CimInstance PowerShell query. It runs before any window exists, so on every launch it would block first paint — the cost lsof does not carry on macOS. |
| Symlink-dependent operations | Degraded | Junctions are used where possible. Operations that need a real symlink require Developer Mode or an elevated shell. | Windows restricts symlink creation to administrators unless Developer Mode is on. |
Two things that are not downgrades, recorded here because they are commonly assumed to be:
- Cursor runs natively on Windows x64. The onboarding flow installs it with
irm 'https://cursor.com/install?win32=true' | iex; there is no WSL requirement. - Reduced-motion preferences are honored. Chromium maps
prefers-reduced-motionto Settings → Accessibility → Visual effects → Animation effects, so no macOS-only API is involved.
Background brain on Windows
The current Windows supervisor is not a Scheduled Task. ade brain start
installs a per-user, per-channel REG_SZ value under:
HKCU\Software\Microsoft\Windows\CurrentVersion\Run
The value starts a hidden, UTF-8-with-BOM PowerShell launcher under the channel's ADE home. That launcher restores the complete resolved brain environment, starts the packaged ADE runtime, and writes an advisory JSON record containing supervisor and runtime process ids. Status validates that the recorded supervisor is a PowerShell process whose command line names the exact launcher before treating it as ADE-owned.
Release proof records these as separate bounded signals, not one inferred "service is running" claim:
- a redacted process record that the channel-qualified HKCU Run value exists;
- a redacted process record derived from
<launcher>.pid.json, with proof-local aliases for bothsupervisorPidandruntimePidrather than numeric PIDs; - a redacted IPC readiness record showing that a client completed initialize on the channel's runtime endpoint after startup or recovery; and
- a bounded startup/recovery log extract correlated by a proof-local alias.
The launcher's wait is sliced, not unbounded. Every 15 s it reads the brain's
heartbeat file (<ADE home>\runtime\heartbeat.json). This is the Windows half
of the cross-platform wedge watchdog — macOS uses a separate com.ade.watchdog
launch agent because launchd has no loop to fold the check into. An absent,
unreadable, or foreign heartbeat is never read as a wedge, so the guard cannot
fire on a brain it does not own.
A stale beat is not enough to stop the child. A kill needs three facts, and the last two exist because modern standby suspends the supervisor and the brain together — on wake, a beat 40 minutes old describes the suspension, not a wedge:
- the beat is older than 90 s and belongs to the child the launcher started;
- the supervisor was awake to watch it go stale. It measures its own
lateness, the one thing a suspended process can still report. When the gap
since its previous poll is at least
max(90 s, 3 × 15 s)and the beat's age fits inside that gap plus the staleness window, the launcher logsmachine sleptand clears any strike. A brain that went quiet days before a ten-minute nap is not explained by the nap, and still dies; - a previous poll already saw this same beat, matched on the beat's own timestamp rather than its age. A brain the OS merely stopped scheduling beats again before the next poll and never reaches the kill; a wedged one is still sitting on the same timestamp one cadence later. A beat that comes back fresh clears the strike, so the two stale polls have to be consecutive.
macOS applies the identical two rules in evaluateBrainHeartbeat. The suspend
floor is the same formula on both platforms — max(staleMs, 3 × the checker's own interval) — not the same number: 90 s on Windows (15 s poll) and 180 s on
macOS (60 s StartInterval), with brainWatcherSuspendFloorMs computing it
there and the rendered PowerShell computing it inline.
ADE_BRAIN_HEARTBEAT_STALE_MS overrides the 90 s staleness threshold on both
platforms, with the same rules: a positive integer wins, anything else keeps the
default, and the result never drops below 30 s. The suspend floor is computed
after the override, so it rescales with it. Only the pickup differs. The macOS
checker is a fresh process every 60 s, so it reads the variable at the next
check. The Windows supervisor reads the variable once when it starts, so a
changed machine variable applies after the brain service restarts. Neither
platform has an override for the poll interval.
The PID JSON is advisory ownership evidence. It is not a readiness file, and an
ONLOGON Scheduled Task is not a current supervisor or readiness mechanism.
The Run entry re-establishes the supervisor at the next user logon; the launcher
waits for the runtime and then exits with it. For an in-session runtime crash,
prove crash detection and recovery through the supported ade brain start or
repair path rather than claiming that the launcher contains an automatic
restart loop.
Stable, Beta, Alpha, and custom service labels get separate value names, launchers, ADE homes, and named pipes. Installation and uninstall also query and remove exact Scheduled Task names created by older preview builds. A Scheduled Task found during migration is legacy residue; it is not the active supervisor contract.
The current-user startup key requires no administrator access. Do not move the entry to HKLM, request elevation merely to start ADE at login, or tell users to repair the current service in Task Scheduler.
First diagnostic pass
Run these from a newly opened PowerShell or cmd window so it sees the current user PATH:
ade brain status --text
ade doctor --json
ade runtime service-status --text
ade report-issue produces the same redacted Markdown report the desktop's
Report issue button does, and it reads local files only — it never starts or
contacts the brain, so it works on a machine where ADE will not come up and on a
headless host with no error screen to press. --open opens a prefilled GitHub
issue. Prefer it over hand-assembled output: paths, the account name, hostnames,
tailnet and .local names, addresses and credential-shaped values are stripped
by redactDiagnosticText over the whole document, and secrets, environment
blocks and pairing PINs are never collected at all. See
storage and recovery.
Before sharing anything you assembled by hand, remove user paths, repository names, machine/device ids, account details, IPs, URLs with query strings, and any credential-shaped value. Prefer reporting the status/error code and ADE version. Do not attach the whole ADE home or a raw database.
Brain is installed but not running
First rule out a brain that is simply still starting. An install that reports
starting has registered the service and left a live child that had not
answered on the pipe inside WINDOWS_HANDOVER_TIMEOUT_MS (15 s — shorter than
the POSIX budget because the supervisor is a process the installer starts and
watches directly). That is not a failed install: the supervisor owns that child,
and restarting it only resets its clock. ade connect says "installed — the
background service is still starting", and the desktop shows brain_starting
with no Repair offered and reopens the project itself once the pipe answers.
Give it up to RUNTIME_SERVICE_YOUNG_BRAIN_MS (120 s) before treating it as
stuck. If it is genuinely not coming up:
- Confirm the channel-qualified value exists under the current user's Run key. Do not paste its command because it contains local installation paths.
- Run
ade brain start, thenade brain status --text. - Confirm the PID record exists under the channel ADE home's
runtime/directory and that the supervisor/runtime are live. Report proof-local PID aliases rather than raw command lines. - If status says the startup entry exists but the supervisor is not running,
run
ade brain stopfollowed byade brain startto rewrite the launcher and entry. - If an old
ADE RuntimeScheduled Task remains, normal install/uninstall should remove it. A cleanup failure is fail-closed; capture only its bounded error text and retry from the same user account.
If a logoff or reboot was part of the failure, verify both process recovery and an initialized client call. A process in Task Manager alone does not prove that the named pipe is healthy.
Desktop cannot attach to the local brain
- Confirm desktop and CLI are the same channel. Stable and Beta intentionally use different ADE homes and pipe names.
- Close only the affected channel, restart its brain, then relaunch that channel.
- A second Windows account must not be able to use the first account's pipe. Access denial is expected isolation, not a reason to weaken named-pipe ACLs.
- Do not publish a full named-pipe path in support artifacts. Record a channel alias and the initialize result.
- If the desktop reports a packaged/runtime build mismatch, reinstall the same signed version before changing local state.
Phone, Relay, or Tailscale cannot connect
crdtSyncAvailablemust be true. If it is false, reinstall/restart the packaged app; Windows pairing is blocked rather than silently running without CRR support.- LAN requires the approved Windows Defender Firewall path and a reachable sync listener. Record the route kind and coarse outcome, never IPs or pairing data.
- Tailscale resolution checks the normal Program Files installation, then PATH.
Use
ADE_TAILSCALE_CLIonly for a non-default install. - Relay is available only while the Windows brain has a current ADE account lease and owns the machine-wide sync-host lease. There is no separate Relay toggle.
- A connected Relay control is not sufficient; directory publication requires the end-to-end self-probe to pass.
- On a physical iPhone, verify both the visible connection and a bidirectional synthetic CRR row roundtrip. Do not treat "Connected" as complete sync proof.
Provider or terminal launch fails
- Reproduce as four separately labelled cases: Windows PowerShell 5.1,
PowerShell 7, cmd, and Git Bash. A generic
powershellresult does not prove either PowerShell version, and cmd results do not stand in for Git Bash. - For each of Claude, Codex, Cursor, OpenCode, and Droid, test authenticated and unauthenticated states, fresh launch, and tracked resume separately. Resume bugs often bypass the fresh structured command path.
- Use a disposable path/prompt containing Unicode, spaces, quotes, dollar and percent signs, ampersands, and backticks.
- Exercise ConPTY resize, Ctrl+C, explicit cancellation, descendant cleanup, and crash restore. Do not use a successful terminal close as the only process-cleanup signal.
- Confirm interrupted sessions expose bounded recovery metadata and actionable recovery instructions. Redact prompts, transcripts, command arguments, account identifiers, and paths independently for every provider.
- Provider credentials are machine-local. Never copy a credential store into an evidence bundle or across a cross-machine handoff.
- The shared credential store (
credentials.json.enc) is sealed with the bare machine key on every platform, never with DPAPI- or keychain-derived material. It is co-owned by the desktop app, the brain and the CLI, and any binding one of them can derive but another cannot locks that other one out permanently. Windows DPAPI (CurrentUser) is symmetric between the desktop app and the scheduled-task brain because both run as the signed-in user — but the DPAPI helper is a PowerShell spawn that can time out, and a timeout used to throw out of a plain credential read. Verify that a DPAPI failure degrades to an "unreadable, may be recoverable" report and never terminates the brain. - Single-writer secrets that want OS-level protection belong in the Electron
safeStorage store (
credentials.safe.enc), which only the desktop app reads.
Standalone brain or installation is damaged
- Exercise the standalone
ade-win32-x64install independently of the desktop:ade brain start,ade brain status --text,ade doctor --json, and the supported update command must all operate from the standalone payload. - A Windows client may bootstrap supported macOS/Linux runtimes. Verify the OpenSSH client prerequisite first and require a bounded actionable error when it is absent. This does not make Windows an SSH-bootstrap target.
- Windows standalone install and
ade brain updateare implemented, but the exact-SHA proof must re-hashade-win32-x64.exe, its native archive,install.ps1, andSHA256SUMSfrom the immutable proof run. Missing or mismatched evidence is a public-release blocker; do not relabel it as a pass or substitute a manual binary copy. install.ps1stages everything under the ADE home, never%TEMP%:<runtime dir>.new/.previousbeside the runtime,ade.new.exe/ade.bak.exebesideade.exe.%TEMP%holds the downloads and nothing else, because it is routinely on a different volume from the user profile (redirected TEMP, a RAM disk, a roaming profile) whereMove-Itemdegrades to copy-then-delete, and because AppLocker and most EDR agents block execution out of%TEMP%outright — which would fail the preflight on every managed corporate machine. Every promotion is a same-directory rename, and the preflight runs the new binary against the staged runtime it will actually load. A failed install prints a stage-aware note: before promotion, the existing install was not touched; after a rollback, the previous one was put back. If the rollback itself failed, the recovery copies are named in the error and are the machine's only copy — do not delete them.- Repair a partial launcher/runtime installation with the product's bounded repair path before testing reinstall or uninstall. Repair must preserve projects and user state; reinstall and uninstall remain separate scenarios with their own ownership checks.
Update does not land
- Confirm
latest.yml, installer, and blockmap match the manifest hashes and the installed app's update authority is the repository that built it. - Preserve the first verified cached download after a lost handoff; ADE clears the cache after a second failure for the same target.
- A checksum, signature, publisher-pin, or timestamp failure must clear unsafe cache data and must not replace the running app.
- Verify the relaunch version, project data, and HKCU supervisor after update. References to "Scheduled Task repair" are stale; only legacy-task cleanup is expected.
Uninstall leaves residue
The uninstaller owns only its channel's background service, launcher/PID record, terminal shim, and the matching user PATH entry. It should not delete projects, another ADE channel, or unrelated PATH entries.
Check, using redacted aliases:
- the channel's HKCU Run value is absent;
- its supervisor/runtime process tree is stopped;
- its launcher and PID record are absent;
- exact legacy Scheduled Task names are absent;
- the installer-owned shim and PATH component are absent; and
- other channels and project data remain.
If cleanup fails, leave the product state intact and report the bounded cleanup error. Do not manually delete broad ADE homes as a support shortcut.
Support bundle boundary
A safe support bundle may contain the proof manifest, the relevant scenario entries, small redacted event extracts, synthetic DB query results, proof-local process/IPC/network summaries, and cropped screenshots. It must not contain:
.ade/secrets, credential stores, tokens, pairing PINs, DPoP material, or signing files;- raw
.db, WAL, or SHM files; - full logs or environment blocks;
- home paths, account or computer names, email addresses, IPs, or device ids;
- chat transcripts, source files, private repository names, or artifact bytes unrelated to the reproduction; or
- raw certificate subjects or certificate files. Store only approved digests needed to compare identity.
Validate release evidence with windows-proof-manifest.mjs before attaching it
to a support or release record.