Repository navigation
feat: run stim-server service and setup on Windows - #3658
Conversation
|
Fresh review of 1df8155 (diff against Medium
Low
The detached updater is correct. In Items 1-3 are actionable, so I am not adding |
|
Human review: new ways to run, persist and kill processes on this machine, and a Stim capability that is new on Windows. Reviewed at 1df8155.
|
|
Addressed in df8fd5b: (1) |
|
Fresh review of df8fd5b (diff against All nine earlier items are fixed as described: Medium
Low
|
|
Human review: new ways to run, persist and kill processes on this machine, and a Stim capability that is new on Windows. Reviewed at df8fd5b.
|
|
Addressed in 322c28d: (1) the mtime/LastRunTime check is gone; the state file counts only when its supervisor pid equals the running instance's |
|
Fresh review of 322c28d (diff against Both items from the previous review are fixed.
Nothing actionable remains. One note, not blocking: when PowerShell itself fails (for example, Constrained Language Mode refuses I am adding |
|
Human review: new ways to run, persist and kill processes on this machine, and a Stim capability that is new on Windows. Reviewed at 322c28d.
|
|
Human review: the maintainer approved the three listed items in chat on 2026-10-13: Stim's ownership of the scheduled task's EnginePID process tree and killing it, the remote updater escaping that tree, and the persistent S4U logon task that lets an approved client update stim-server. |
e3cc10c to
6ebc3b5
Compare
322c28d to
2558fa4
Compare
6ebc3b5 to
ae91a04
Compare
2558fa4 to
95780c6
Compare
95780c6 to
7ac4cb5
Compare
Description
Stacked on #3640 (Windows build workers), which forks from #3631 (Linux
serviceandsetup). The diff here is againstfeat/3616-windows-android-build-worker.On Windows,
stim-server serviceandstim-server setuprefused because there was no service manager backend. A Windows worker had to be started by hand: it did not start at logon, did not restart after a crash, and a client could not update it.Solution
This adds a third
ServiceManagerbackend,taskscheduler, next to launchd and systemd.service.tskeeps all the policy; the backend only answers what differs. It never needs administrator rights.I chose the mechanism by probing a real
windows-latestrunner:runas /trustlevel:0x20000),schtasks /Create /XMLregistered both an interactive task and an S4U task.Register-ScheduledTaskfailed ("Cannot connect to CIM server. Access denied"), so the backend usesschtasks.RestartOnFailureset, was not run again within 5 minutes.Stop-ScheduledTaskends only the action process. Children of the action and WMI-created processes survived it.From those results:
%LOCALAPPDATA%\Stim\services\<label>.json, written byrenderand edited byrewrite. Logs go to%LOCALAPPDATA%\Stim\Logs\<label>.log, and versions and update outcomes live under%LOCALAPPDATA%\Stim\services\<label>\.\Stim\<label>uses an S4U principal with LeastPrivilege, a logon trigger for the user's SID, and normal priority (Task Scheduler's default of 7 is below normal for CPU, I/O and memory). It starts at logon and keeps running after sign-out. Its action runsstim-service-runner.mjs, a supervisor that imports only Node built-ins.installcopies the supervisor beside the service, so it does not depend on the server version the service runs.RestartSec.supervisor.json. Its own errors, such as an unreadable service file or a state file it cannot write, go tosupervisor.logbeside it (and into the last exit code); it then carries on instead of exiting.jobtrusts that file only when the supervisor pid it names is theEnginePIDof the task's running instance, read through the Task Scheduler COM API (GetInstances(1), which includes hidden tasks). Task Scheduler starts that process itself, so this needs no process identity token, mtime or clock comparison. Core'sinspectProcessStartis Darwin-only. On the runner,EnginePIDmatched the supervisor's pid.stopends the supervisor's process tree withtaskkill /T /F, then runsschtasks /End.unloadfinds port listeners withnetstat -anoinstead oflsof, treating TCP rows with no foreign address as listeners because netstat localizes the state column.--scope.detachedstarts the updater through a short-lived middle process.taskkill /Tfollows only live parents, so the updater escapes the tree. A waiting process stays in the tree so the server sees the update end.service.ts:node npm-cli.js, because Node will not run thenpm.cmdshim without a shell.--no-save. Measured on the runner, npm 10.9.9 otherwise fails in two ways: run from a directory with no package.json, it looks for one there (ENOENT); run from the project, saving the root package.json throws "Cannot read properties of undefined (reading 'spec')". On macOS the same install passes.--no-savewith the project as the working directory passes. I found no npm issue that matches.%SystemRoot%\System32\tar.exereplaces/usr/bin/tar, so Git for Windows' GNU tar on PATH is not picked, andstableNodelooks fornode.exe.setupaccepts Windows like Linux:--buildonly, no Stim Host, no permission steps, and Windows remedies for Tailscale, the JDK and the Android SDK.--device-hostis refused ("On Windows, run setup with --build only.").node.exeshim. This follows the product decision: Stim keeps PATH as the user set it.installdetects when the firstnode.exeon the service's PATH is not the Node that runs the server, says that a shim such as pnpm's breaks builds withfingerprint-mismatch, and gives the exact--path-prependthat fixes it. The docs say the same.server.workerRootalready accepts Windows absolute paths in feat: let Windows machines take offloaded Android builds #3640.Known limits:
stopis forceful. Windows has no SIGTERM, so the server's shutdown handlers do not run; builds it was running are ended with its tree.Test plan
All of this ran on a real
windows-latestrunner (stim-windows-runner). The client was this branch's CLI on a Mac, using a scratchSTIM_HOMEand a bare React Native 0.87 app. trailhead does not fit Windows path limits; see #3641.runas /trustlevel:0x20000, which drops the Administrators group),service install --serveinstalled and started the service (exit 0), andservice statusread it.service install --serve --path-prepend "C:\Program Files\nodejs"gavestim-server 1.20.2 answers on 127.0.0.1:7787andTailnet route: https port 7443, already present.schtasks /QueryshowedStatus: Running,Logon Mode: Interactive/BackgroundandRun As User: runneradmin. Without--path-prepend, install printed the shim note namingsetup-pnpm\bin\node.exe.supervisor.logrecorded… is not a service file install wrote., status showed it as the last exit, and once the file was restored the server came back (running (pid 8380),runs: 2).taskkill /PID <server> /F. Status readruns: 1, last exit 1with no pid, then, about 30 s later,running (pid 7312)withruns: 2.service update --from <packed packages>switched toversions\1.20.2-…, andservice rollbackswitched back.could not be started: … (last exit code 1) … Switched back, and the service was healthy on the previous server.server.acceptClientBuilds=trueon the runner, and a scratch stim-server on the Mac with a control pairing sendingmachines.update.start.unreachable: 502while it did). The updater survived the restart:last-update.jsonreadsok: truewithnow runs … from versions\1.20.2-f73858d3bc39301c-…, andmachines.update.statusreports it.stim-server setup --client <Mac node> --ticket <t> --expires <…> --build --yeswaited for approval.STIM_ACCESS_TICKET=<t> stim doctor --fixon the Mac sent the request, and setup finished withapproved … for buildsandready to build.--device-hostexited 1 with the refusal above.stim-windows-runner approved True [].stim android --remote-build stim-windows-runnergavebuilt on stim-windows-runner in 9m42s, installed on the Mac's emulator, andbundle loaded. After a reinstall, an incremental build took 21.2s.Kept the tailscale serve route on https port 7443).Unit tests cover the service file's round trip and edits, Windows argument quoting and the task XML,
netstatlistener parsing, and setup on Windows, with the Linux setup tests parameterized.Fixes #3650