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:
@@ -258,9 +258,10 @@ else still was).
|
||||
| `%ProgramData%\RunicGateway\install.json` | As above |
|
||||
| `%ProgramData%\RunicGateway\patches\` | As above |
|
||||
| `%ProgramData%\RunicGateway\patches\originals\` | As above |
|
||||
| `%ProgramData%\RunicGatewayackups\<timestamp>\` | As above |
|
||||
| `%ProgramData%\RunicGateway\backups\<timestamp>\` | As above |
|
||||
| `%ProgramData%\RunicGateway\uo-link.db` | The sidecar's SQLite store |
|
||||
| Service `RunicGatewayLink` | Automatic start, restart on failure, running as `NT SERVICE\RunicGatewayLink` |
|
||||
| `%ProgramData%\RunicGateway\uo-link-sidecar.<date>.log` | The service's log. A Windows service has no console to write to, so it logs here instead; rolled daily, seven kept. A foreground run still logs to stdout as usual |
|
||||
| Service `RunicGatewayLink` | Automatic start, restart on failure, running as `NT SERVICE\RunicGatewayLink`. Needs a sidecar **v1.2.0 or newer** — see [Troubleshooting](#troubleshooting) on error 1053 |
|
||||
|
||||
**Inside your ServUO tree** (added by the overlay sync — 24 files):
|
||||
|
||||
@@ -630,7 +631,8 @@ release tarball is gone, and the report tells you to diff against them.
|
||||
| Website logs `409` from the sidecar | Protocol mismatch: the number in Admin → Shard does not match the sidecar's. The sidecar rejects rather than mis-parsing. Set the field to what `/health` reports (`protocol`). If the *sidecar* and *overlay* disagree, you have a hand-assembled pair — reinstall from a bundle. |
|
||||
| `401` from the sidecar | Wrong or missing auth token. Read the live one back with `uo-link-sidecar --print-config --config <path>`; do not retype it from a screenshot. |
|
||||
| **"service NOT REGISTERED" at the end of an otherwise successful run** | The host has no service manager the installer can drive — most often no systemd (a container, or a distro that never had it), or the `runicgateway` user could not be created. The binary and config *are* installed; the run prints the exact unit and commands to finish by hand. It never falls back to running the service as root or `LocalSystem`. |
|
||||
| Service registered but stops immediately | It cannot read its config. On Windows check that `sc qc RunicGatewayLink` shows `--config` in `BINARY_PATH_NAME` and that `NT SERVICE\RunicGatewayLink` has read access to `sidecar.toml`; on Linux check the `runicgateway` user can read `/etc/runicgateway/sidecar.toml` and write `/var/lib/runicgateway/`. |
|
||||
| **Windows: `sc start` fails with 1053, "the service did not respond in a timely fashion"** | Almost always a **sidecar older than v1.2.0**, which cannot start as a service no matter how correct its config. 1053 is a handshake failure, not a crash: Windows waited 30 seconds for the process to identify itself to the service control manager, and a sidecar built before service support was added never does. Check with `"C:\Program Files\RunicGateway\uo-link-sidecar.exe" --version`. Tell-tale signs: `sc query` shows `SERVICE_EXIT_CODE : 0` (nothing crashed), and running the same binary in the foreground with the same `--config` works perfectly. |
|
||||
| Service registered but stops immediately | Distinct from 1053 above — here the process really did exit. On Windows read `%ProgramData%\RunicGateway\uo-link-sidecar.<date>.log`, which is where a service logs since it has no stdout, and check that `sc qc RunicGatewayLink` shows `--config` in `BINARY_PATH_NAME` and that `NT SERVICE\RunicGatewayLink` has read access to `sidecar.toml`; on Linux check the `runicgateway` user can read `/etc/runicgateway/sidecar.toml` and write `/var/lib/runicgateway/`, and read `journalctl -u runicgateway-link`. |
|
||||
| A patch will not apply | Expected on a hand-modified shard. The base install is unaffected; you lose only the two features in [§4](#4-the-patch-tier-optional). Apply the hunks by hand if you want them. |
|
||||
| `vendor.sale` events never arrive despite patching | The `EventSink.cs` patch is a **core** change. A shard restart is not enough — rebuild the solution (`dotnet build ServUO.sln`). |
|
||||
| Sidecar writes its database somewhere unexpected | A relative `[store] path` resolves against the directory holding `sidecar.toml` — not the working directory. Run `--print-config` to see the absolute path it will actually use. |
|
||||
@@ -800,8 +802,13 @@ icacls "$env:ProgramData\RunicGateway" /grant 'NT SERVICE\RunicGate
|
||||
sc.exe start RunicGatewayLink
|
||||
```
|
||||
|
||||
Three things there are easy to get wrong:
|
||||
Four things there are easy to get wrong:
|
||||
|
||||
- **The sidecar must be v1.2.0 or newer.** Earlier builds are plain console programs, and the
|
||||
Windows service control manager cannot supervise one: it waits 30 seconds for the process to
|
||||
identify itself, then fails the start with **1053** even though the process is running and healthy.
|
||||
From v1.2.0 the same binary does both — started by the SCM it runs as a service, started from a
|
||||
shell it runs in the foreground, with no flag to choose between them.
|
||||
- **The config path goes in `binPath`, not in a machine environment variable.** `sc.exe` has no
|
||||
per-service environment, and a machine-wide `UOLINK_CONFIG` would be inherited by every process on
|
||||
the host and survive an uninstall. Never leave the config path to the default — it is relative to
|
||||
@@ -813,6 +820,9 @@ Three things there are easy to get wrong:
|
||||
this service. Omit `obj=` and you get the most privileged local identity there is, for a process
|
||||
listening on two TCP ports.
|
||||
|
||||
Once it is running, `%ProgramData%\RunicGateway\uo-link-sidecar.<date>.log` is where it logs — a
|
||||
service has no console to write to. Seven days are kept.
|
||||
|
||||
### A5. Connect the website, start the shard, verify
|
||||
|
||||
Exactly as in [§5](#5-connect-the-website) and [§6](#6-start-servuo-and-verify): paste the four
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user