diff --git a/installer/INSTALL.md b/installer/INSTALL.md
index 65a198c..c8fc730 100644
--- a/installer/INSTALL.md
+++ b/installer/INSTALL.md
@@ -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
diff --git a/installer/PLAN.md b/installer/PLAN.md
index 2b068c0..c931e14 100644
--- a/installer/PLAN.md
+++ b/installer/PLAN.md
@@ -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
