Windows: install the background service without Administrator#1211
Open
RhysSullivan wants to merge 2 commits into
Open
Windows: install the background service without Administrator#1211RhysSullivan wants to merge 2 commits into
RhysSullivan wants to merge 2 commits into
Conversation
The Windows `executor install` / `executor service install` always required an elevated PowerShell. It registered the daemon as a boot-triggered Scheduled Task (AtStartup + S4U + RunLevel Highest), and Task Scheduler only lets an Administrator create that shape. macOS (launchd RunAtLoad) and Linux (systemd --user) both install per-user with no elevation, so Windows was the odd one out. Default to a per-user logon task instead: a LogonTrigger + InteractiveToken principal at LeastPrivilege, which a standard user may register for themselves. This matches the launchd/systemd behavior (start at login, unprivileged). The old boot-before-login behavior is preserved behind an explicit `--boot` flag, which still needs an Administrator shell and says so. Register via schtasks.exe rather than the PowerShell *-ScheduledTask cmdlets. Those cmdlets reach Task Scheduler over CIM/DCOM, whose local-activation check fails with Access denied (0x80070005) when the compiled binary spawns the helper on a non-interactive window station; schtasks uses Task Scheduler RPC and has no such dependency. status/uninstall/restart and the orphaned-listener cleanup move off CIM too (schtasks query + netstat/taskkill). A logon task runs in the user's interactive session, so launch the daemon through a hidden wscript shim to avoid flashing a console window on the desktop at every login; the shim waits on the wrapper so the task stays Running and RestartOnFailure is preserved. Verified on Windows Server 2022 as a standard, non-admin user: `--boot` is correctly refused, the default install registers a logon task, the daemon comes up, and /api/health returns 200.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-marketing | dac3e0a | Commit Preview URL Branch Preview URL |
Jun 29 2026, 05:01 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-cloud | dac3e0a | Jun 29 2026, 05:02 PM |
Contributor
Cloudflare preview
Sign-in is Cloudflare Access (one-time PIN to an allowed email). The preview has its own database and encryption key; it is destroyed when this PR closes. |
@executor-js/cli
@executor-js/config
@executor-js/execution
@executor-js/sdk
@executor-js/codemode-core
@executor-js/runtime-quickjs
@executor-js/plugin-file-secrets
@executor-js/plugin-graphql
@executor-js/plugin-keychain
@executor-js/plugin-mcp
@executor-js/plugin-onepassword
@executor-js/plugin-openapi
executor
commit: |
Address review on non-English Windows: - status() derived "running" from the schtasks `Status: Running` line, but both the label and the word are localized (fr: `État : En cours d'exécution`), so it always read as not-running on a non-English install. Detect via the locale-invariant SCHED_S_TASK_RUNNING result code (267009 / 0x41301) instead, extracted into the pure, tested parseSchtasksRunning(). - parseNetstatListenerPids matched the localized `LISTENING` state word. Identify a listener by its wildcard/zero remote endpoint (0.0.0.0:0 / [::]:0) instead; TCP and addresses are not localized. - `--boot` is documented Windows-only but was silently ignored on macOS/Linux. Warn when it is passed on a non-Windows platform. Tests add fr-FR / de-DE fixtures for both parsers. Verified on the Windows guest against a real running task's schtasks output: the old Status/Running regex regresses to false under fr-FR rendering while the 267009-based check stays correct.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Windows: install the background service without Administrator
executor install/executor service installon Windows always required anelevated PowerShell. It registered the daemon as a boot-triggered Scheduled Task
(
AtStartup+S4U+RunLevel Highest), and Task Scheduler only lets anAdministrator create that shape. macOS (launchd
RunAtLoad) and Linux(
systemd --user) both install per-user with no elevation, so Windows was theodd one out.
What changed
LogonTrigger+InteractiveTokenprincipal at
LeastPrivilege, which a standard user is allowed to register forthemselves. This matches the launchd/systemd behavior (start at login,
unprivileged).
--boot:executor service install --bootstill installs the old
BootTrigger/S4U/HighestAvailabletask for trueheadless reboot survival. That genuinely needs Administrator, and the command
says so when run unelevated.
schtasks.exe, not the PowerShell*-ScheduledTaskcmdlets.Those cmdlets reach Task Scheduler over CIM/DCOM, whose local-activation check
fails with
Access denied(0x80070005) when the compiled binary spawns thehelper on a non-interactive window station.
schtasksuses Task Scheduler RPCand has no such dependency.
status/uninstall/restartand theorphaned-listener cleanup move off CIM too (
schtasks /query+netstat/taskkill).so the daemon is launched through a hidden
wscriptshim to avoid flashing aconsole window on the desktop at every login. The shim waits on the wrapper, so
the task stays
RunningandRestartOnFailureis preserved.Verified on real Windows
Windows Server 2022, running as a standard, non-admin user (
execstd, Mediumintegrity, no UAC token):
executor service install --bootis correctly refused without Administrator.executor service install(the new default) succeeds: registers a logontask (
Logon Mode: Interactive only,Schedule Type: At logon time), thedaemon comes up, and
/api/healthreturns HTTP 200.Final state (still)
Tests
apps/cli/src/service.test.tsupdated: the default task XML assertsLogonTrigger/InteractiveToken/LeastPrivilege(and noBootTrigger/S4U/HighestAvailable),--bootasserts the boot/S4U shape, thewscriptshim iscovered, and
netstatlistener parsing replaces the old CIM-script test.