docs(installer): correct the Windows service decision, and the 1053 advice

PLAN.md §8 recorded that `sc create` against the plain console sidecar worked
and needed no change to `link`. The first Windows install disproved it: 1053,
"a timeout was reached (30000 milliseconds) while waiting for the service to
connect", with SERVICE_EXIT_CODE 0.

The premise was a false symmetry with systemd. systemd supervises any
foreground process; the Windows SCM supervises only one that calls
StartServiceCtrlDispatcher within ~30 seconds. Record the reversal and what it
costs: link gains a Windows service entry point, kept at the edges so the whole
sidecar stays shared and Cargo builds neither Windows crate for Linux.

INSTALL.md:
- Troubleshooting gains a 1053 row naming the real cause (a sidecar older than
  v1.2.0) and the two tell-tales that distinguish it from a crash: exit code 0,
  and a foreground run of the same binary working fine.
- The existing "stops immediately" row said the same wrong thing; it now covers
  the genuine-crash case only, and points at the log file and journalctl.
- §3 and Appendix A4 document the service log, and A4 states the version floor.
- Fixes a literal 0x08 byte in the backups path row, which rendered as
  `%ProgramData%\RunicGatewayackups\` — the backslash had been eaten.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-07 13:36:51 -05:00
parent 00d476c00b
commit 7c1a88febb
2 changed files with 50 additions and 13 deletions

View File

@@ -1284,15 +1284,42 @@ mismatched pair from being published as a bundle — which is the mechanism that
different host, or hard-assume co-location?
Resolved and moved into §1 / §2.2 / §5: uninstall scope, and minimum ServUO version.
**Resolved — Windows service mechanism** (was question 1). `sc create` against the plain console
binary, as recommended: it works on a stock host, ships nothing extra, and needs no change to
`link`. A WinSW/NSSM shim would be a third binary to keep current, and a native `--service` mode
using the `windows-service` crate would put Windows service plumbing inside a component whose whole
job is being platform-agnostic. Restart semantics turned out to be adequate —
`sc failure … actions= restart/5000` is the direct counterpart of systemd's `Restart=on-failure` /
`RestartSec=5`. What the recommendation did *not* anticipate is the service identity: plain
`sc create` runs as `LocalSystem`, so Phase 2 registers with `obj= "NT SERVICE\RunicGatewayLink"`
instead (see [Phase 2 as built](#phase-2--uo-link-install-and-service)).
**Resolved — Windows service mechanism** (was question 1). Registration is `sc create` with
`obj= "NT SERVICE\RunicGatewayLink"` (plain `sc create` would run as `LocalSystem`, which the Linux
half pointedly does not do), `sc failure … actions= restart/5000` as the counterpart of systemd's
`Restart=on-failure` / `RestartSec=5`, and the config pinned in `binPath` rather than in a
machine-wide environment variable. No WinSW/NSSM shim: that would be a third binary to keep current.
**Corrected 2026-08-07 — the sidecar needs its own service mode after all.** This section previously
recorded that `sc create` against the *plain console binary* worked and needed no change to `link`.
It does not, and the first Windows run proved it: `sc start` failed with **1053** and the event log
read *"a timeout was reached (30000 milliseconds) while waiting for the … service to connect"*,
with `SERVICE_EXIT_CODE : 0` — the process had started fine and simply never spoke to the SCM.
The premise was a false symmetry with systemd. systemd supervises *any* foreground process; the
Windows SCM supervises only a process that calls `StartServiceCtrlDispatcher` within ~30 seconds and
then reports its own state transitions. There is no third option where `sc.exe` adopts an arbitrary
console executable — it is a service-aware binary or a shim, and the shim was already rejected.
So `link` gains a Windows service entry point (the `windows-service` crate, behind
`[target.'cfg(windows)'.dependencies]`). The objection that this puts Windows plumbing inside a
platform-agnostic component is answered by keeping it *only* at the edges: `app::run` is the whole
sidecar and is shared, while `windows.rs` and `unix.rs` do nothing but start it and tell it when to
stop. Nothing platform-specific reaches the shared path, and Cargo neither resolves nor builds the
Windows crates for Linux.
Consequences worth knowing:
- **One binary, no `--service` flag.** The dispatcher is tried first; failing with
`ERROR_FAILED_SERVICE_CONTROLLER_CONNECT` (1063) means "not started by the SCM" and falls through
to a normal foreground run. `cargo run` and a hand-run diagnostic are unchanged.
- **A service has no stdout**, so in service mode the sidecar logs to a daily-rolled file beside its
config instead of into the void.
- **`Running` is reported only once the shard port is bound and the store is open**, so a bad config
fails the *start* rather than flapping Running → Stopped, and a failed run leaves a nonzero
`SERVICE_EXIT_CODE` behind rather than the misleading `0` above.
- **A sidecar older than v1.2.0 can never start as a service on Windows**, however good its config.
The installer says so by name when it sees 1053.
**Resolved — branch targeting for the new repo** (was question 4). The v3 cutover landed:
`servuo-plugins#6` merged, so that repo's `main` and `edge` agree at protocol 3. The release