The installer crate now implements the whole command surface INSTALL.md
published before the binary existed, so this records what Phase 4 turned
out to be and corrects two places where the plan and the guide had drifted
apart.
PLAN.md
- Status header: Phases 1–4 are on `edge`; the edge → main cutover now
cuts a binary that does everything INSTALL.md describes, with Phase 5
being packaging polish rather than capability.
- A Phase 4 "as built" section: why `update` is the install pipeline in
a different mode rather than a second implementation, why it neither
reprints the token nor stays quiet about a protocol change, the tier's
scope under `update` (re-resolve what was applied, without re-asking;
name what is new), how `doctor` asks the binary the way the service
does, the exit-code rule and why a stopped shard is a ⚠ while a
running one that has not dialed in is a ✗.
- §5's uninstall table: the cached patch set and patches/originals/ move
from "removed" to "kept". The report that command prints tells the
operator to diff against those originals — deleting them made the
advice impossible to follow within one command's output. `--purge`
removes them.
INSTALL.md
- §2: exit codes stated (`doctor` and `uninstall` use 1 for a completed
run that found something wrong), `--patches` now applies to `update`,
`--yes` means yes on `uninstall`, `--purge` covers the patch cache.
- §7 doctor: the real row set, what ✓/⚠/✗ mean, that it writes nothing
and is safe to run with the shard up.
- §7 update: it updates the tree install.json names, needs the shard
stopped, does not reprint the token, calls out a protocol change, and
what it does and does not do with the patch tier.
- §7 uninstall: what survives, that edited files are flagged in the
listing, the confirmation's default, and where the report file lands.
Co-Authored-By: Claude <noreply@anthropic.com>
PLAN.md gains a "Phase 3 as built" section covering the decisions the plan had
left open, and §2.2, §2.2.1 and §7.0 are brought in line with what shipped:
* The engine is fully native. §2.2.1 wrote rung 1 as "apply verbatim with
git apply", but §1 chose the release tarball so there would be no git on the
shard host, and rung 2 needs a native applier anyway. Rung 1 keeps its
stronger verdict and shares rung 2's write path. On the real files this is
not academic — the shipped patches are CRLF and two of their three targets
are LF, so git apply refuses patches the installer places correctly.
* §7.0 documents `patch_tier` in the overlay manifest. Which patches form one
unit, which companion follows which, whether a core rebuild is needed and
what declining costs are not derivable from a diff, so the release declares
them and adding a patch regenerates metadata rather than an installer.
* §2.2 gains the pre-image cache and the widened patch cache, and §2.2.1 gains
the second, per-feature level of the all-or-nothing rule.
INSTALL.md's illustrated tier output is replaced with the real thing, the
status banner now says `install` is complete, §3's path tables list
patches/originals/, and Appendix A2 names the line-ending trap that makes
git apply refuse a patch whose region is visibly untouched.
Refs: RunicGateway/installer#6, RunicGateway/servuo-plugins#10
Co-Authored-By: Claude <noreply@anthropic.com>
Phases 1 and 2 now live on the installer repo's `edge` branch, so PLAN.md's
status, the config-path section, and the operator guide all move with them.
PLAN.md
- Status: phases 1 and 2 built. The `edge -> main` cutover now follows Phase 3
rather than Phase 2, because INSTALL.md §4 describes the patch tier as part of
the run and a release that answers "not implemented" to all of it is the same
half-capable binary that kept Phase 1 off `main`.
- §2.3: the service definition always pins the config path, but only Linux pins
the database. On Windows config and data share a directory, so the sidecar's
own anchoring rule already lands it correctly — and `sc.exe` offers no
per-service environment, only a machine-wide one that every process inherits
and that outlives an uninstall.
- New "Phase 2 as built" section: the virtual service account, the config
lockdown and why its two halves straddle registration, `--verify` running no
part of the sidecar half, the protocol check against the installed binary,
`RUNICGATEWAY_STATE_DIR` relocating the binary and suppressing service
registration, degrading to a printed recipe with no root/LocalSystem fallback,
and the token never entering install.json.
- §8 question 1 (Windows service mechanism) resolved: `sc create`, as
recommended — plus the service identity the recommendation did not anticipate.
INSTALL.md
- Status banner: what is built, and that the patch tier is the remaining gap.
- §2: the illustrated run matches the sidecar block the binary actually prints.
- §3: a table of how each platform pins config and database, the dedicated
service account on both, and the fact that sidecar.toml's permissions are
restricted because it holds the auth token.
- Appendix A4: the Windows recipe now matches what the installer does —
`--config` in binPath (single-quoted so PowerShell keeps the inner quotes),
`obj=` for the virtual account, the icacls lockdown before and grants after,
and no machine-wide environment variables.
- Troubleshooting: a row for a run that could not register a service, and one
for a service that starts and immediately stops.
Co-Authored-By: Claude <noreply@anthropic.com>
PLAN.md moves from "Phase 0 complete, no code exists" to "Phase 1 built, on
edge", and the Phase 1 section gains an "As built" block in the same shape as
the Phase 0 entries — covering the decisions that were not already settled by
the design: why the crate lands on `edge` instead of `main`, why the library
target is not named after the binary (Windows UAC installer detection makes
`cargo test` unrunnable under that name), the dependency choices that follow
from the MinGW cross-build, path-based rather than name-based shard-running
detection, reading ServUO's version from Server/AssemblyInfo.cs, and the two
rules the smoke test corrected — install.json recording a state rather than the
run's verb, and the Bridge.cfg keep comparing against the last hash deployed
rather than the last hash seen.
INSTALL.md gains the same status note and one troubleshooting row: Windows
elevates the binary on launch because its file name contains "install", which
is expected and needs no action beyond running from an elevated shell.
Co-Authored-By: Claude <noreply@anthropic.com>
The patch tier refused on a whole-file hash mismatch, which is the wrong
question: the three patches touch three small regions of three large files, so
an operator who edited Logging.cs somewhere else entirely was handed a manual
patch job they did not need. Hand-modified shards are the norm, so that refusal
covered most of the audience.
Replace the single hash test with a four-rung ladder (PLAN §2.2.1), cheapest and
safest first:
0 post-patch text already present -> no-op, keeps re-runs idempotent
1 whole file matches the pre-image -> apply verbatim
2 file differs, patched region is still byte-identical -> apply at the
matched offset
3 anything else -> do not touch the file; print the hunk to apply by hand
Rung 2 needs no new metadata: a unified diff already carries the stock text of
the region it edits (context lines plus the '-' lines). Guardrails keep it from
becoming a fuzzy apply -- exact match with only CRLF/trailing-whitespace
normalisation, exactly one occurrence or it fails, line numbers advisory only,
and all-or-nothing per patch file so a half-patched EventSink.cs cannot happen.
install.json records which rung applied each patch, and doctor and uninstall
report it.
This retires the blanket 57.4-only version gate, so PLAN gains §2.2.2 to draw
the line the ladder does not: content matching is a mechanical guarantee about
where text lands, not a support commitment. 57.4 stays the only supported
version. A non-57.4 tree may attempt the tier, but unsupported, untested and not
guaranteed -- behind a loud banner, a prompt defaulted to no, and its own
--patches-unsupported-servuo flag, because a bare --patches can be hit by
accident in a copied script. The unsupported marker persists into install.json,
every later doctor run, and the uninstall report.
INSTALL.md gets the operator-facing half: a block-quoted warning naming the
silent-script-build failure mode, the updated prerequisite row, prompts and flag
table, and a sample run showing all three outcomes.
Co-Authored-By: Claude <noreply@anthropic.com>
Closes the last Phase 0 item. INSTALL.md is written before the installer
binary on purpose: everything it installs is already released (0.1-0.3), so
the guide is not speculation about a tool that might exist - it is the
specification of what the run asks, where it writes, what it prints, and what
the operator does next.
It is useful today. Appendix A is the same deployment done by hand - bundle
fetch, tarball verify and overlay copy, the optional patch tier,
--print-config provisioning, systemd unit / sc create - composed from the
released artifacts' actual contents and the sidecar's config and CLI source.
That appendix doubles as Phase 1's acceptance test.
Writing it settled four things the plan had left implicit, now recorded in
PLAN.md:
- The installer does not install itself; day-two commands run from the
downloaded binary.
- The flag surface: --servuo, --patches/--no-patches, --host, --site-url and
--yes join the --verify/--bundle/--purge the plan already named, so every
prompt has a non-interactive equivalent.
- A modified Config/Bridge.cfg is reported, not overwritten - one deliberate
deviation from deploy.ps1, whose overwrite-on-hash-differs rule is right for
a developer and would silently revert an operator's whole shard config on
update. install.json's recorded hashes are what make the distinction
possible.
- Remote-website deployments: widen [web] bind, firewall it to the site's
address, front it with TLS or a VPN off a trusted network. [shard] bind
stays on loopback because that socket carries commands into the game.
Also adds the missing installer/ section to the docs index, and refreshes two
stale examples in PLAN.md (overlay file count, sidecar version).
Co-Authored-By: Claude <noreply@anthropic.com>