docs(installer): record Phase 0 progress and the overlay manifest

Tracks what actually landed while starting the installer plan, and corrects
the parts of the plan that the work proved wrong or stale.

Progress:

  A Phase 0 status table at the top, so the plan says where it is rather
  than needing a reader to reconstruct it from PR links.

  Phase 0 item 1 (§5) now records the release workflow as built, including
  its three deviations from link's copy — structural gates instead of build
  gates, no bump commit and therefore no push to main, and overlay.toml as
  the home for the declared protocol version. Plus the fixed tarball prefix
  and why: the installer would otherwise have to parse the version it is
  trying to read.

New §7.0 documents the overlay manifest as generated, and states plainly the
two things about it that carry weight: `protocol` is hand-maintained and has
to be (nothing in CI can derive it, which is exactly why §7.1's gate 1 has
something to compare), and `files` is what lets `doctor` distinguish
"operator edited a deployed file" from "the overlay moved on".

Corrections:

  §2.6 the plugin's protocol version now has a home (overlay.toml), and
        servuo-plugins now has a release workflow.
  §7.2  the dispatch step is deliberately deferred to Phase 0 item 3.
  §7.4  no longer "open risk" — the v3 cutover merged. The rule it motivated
        (never hardcode a protocol version) is restated as permanent rather
        than as a workaround for a mid-flight cutover.
  §8    open question 4 (branch targeting) resolved: servuo-plugins#6 merged,
        main == edge, everything targets main.

Version examples in §3, §5 and §7.1 said uo-link v3.x.y / 3.0.1, conflating
the release version with the protocol version. link is actually at v0.3.0 —
the two are independent, and the bundle names release versions, so an example
implying they track each other is actively misleading. Now uses the real
values (link 0.3.0, overlay 0.1.0, 30 overlay files).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-04 09:28:23 -05:00
parent 8322e8318c
commit f0975df598

View File

@@ -1,8 +1,17 @@
# Runic Gateway Installer — plan
Status: **planning**. No installer code exists yet. This document is the design of record; it
supersedes the informal overview it grew out of, which described a ServUO integration that does not
match how `servuo-plugins` actually ships (see [Corrections](#corrections-to-the-original-overview)).
Status: **Phase 0 in progress.** No installer code exists yet. This document is the design of record;
it supersedes the informal overview it grew out of, which described a ServUO integration that does
not match how `servuo-plugins` actually ships (see
[Corrections](#corrections-to-the-original-overview)).
| Phase 0 item | State |
|---|---|
| 0.1 `servuo-plugins` release workflow | 🟨 In review — [servuo-plugins#7](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/7) |
| 0.2 `link` installable (data paths + `--print-config`) | ⬜ Not started |
| 0.3 Bundle CI in the installer repo | ⬜ Not started |
| 0.4 This file + `INSTALL.md` | 🟦 This file exists; `INSTALL.md` waits on the shape settling |
| — Repo bootstrap (governance + CI) | 🟨 [`RunicGateway/installer`](https://gitea.whitlocktech.com/RunicGateway/installer) created; workflows in [installer#1](https://gitea.whitlocktech.com/RunicGateway/installer/pulls/1) |
---
@@ -134,15 +143,16 @@ operators.
### 2.6 Prerequisites the overview assumed away
- **`servuo-plugins` has no release workflow.** Only `link` does. "Pull latest repository" is
replaced by a release tarball, which has to be built first (Phase 0).
- **`servuo-plugins` had no release workflow.** Only `link` did. "Pull latest repository" is replaced
by a release tarball, which had to be built first Phase 0 item 1, now in review
([servuo-plugins#7](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/7)).
- **arm64 is not buildable today.** `link/release.yml` cross-compiles only
`x86_64-unknown-linux-gnu` and `x86_64-pc-windows-gnu`. An arm64 `.deb` needs another cross
toolchain.
- **The compat matrix has no home.** `PROTOCOL_VERSION` currently lives only in
`link/sidecar/src/main.rs`. The sidecar publishes it via `X-UOLink-Version` and `/health`, and the
website stores an expected value — but the *plugin's* protocol version is not queryable before
boot. See §7.
- **The compat matrix has no home.** `PROTOCOL_VERSION` lives in `link/sidecar/src/main.rs`. The
sidecar publishes it via `X-UOLink-Version` and `/health`, and the website stores an expected
value — but the *plugin's* protocol version is not queryable before boot. Phase 0 item 1 gives it
a home: `servuo-plugins/overlay.toml`, declared into the overlay manifest. See §7.0 / §7.1.
---
@@ -157,7 +167,7 @@ Runic Gateway Installer v1.0.0
├── runicgateway-installer-linux-x86_64
└── SHA256SUMS
uo-link v3.x.y (existing release, extended)
uo-link v0.x.y (existing release, extended)
├── uo-link-sidecar-windows-x86_64.exe
├── uo-link-sidecar-linux-x86_64
├── runicgateway-link_<ver>_amd64.deb (Phase 5)
@@ -222,6 +232,26 @@ Repo work that must land before an installer can exist.
consume only `{version, changelog, artifacts}`). The adapter half produces
`runicgateway-overlay-<ver>.tar.gz` containing `overlay/`, `patches/`, and a `manifest.json`
(version, commit, per-file SHA256, declared protocol version, minimum ServUO version).
As built ([servuo-plugins#7](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/7)),
with three deviations from `link`'s copy that each fell out of the repo rather than being chosen:
- **No build gates, structural gates instead.** Nothing in that repo can be compiled without
ServUO reference assemblies, so CI asserts what it honestly can: `Bridge.cfg` and the Bridge
scripts present, `Scripts.csproj` present (its absence ships code that never compiles while
ServUO reports success — §2.1), every `.patch` parseable via `git apply --stat`, and each
patch's companion `.cs` present.
- **No bump commit, so no push to `main`.** `link` writes the version into `Cargo.toml` because
the binary embeds it; the tarball embeds nothing but the generated manifest, so the tag *is*
the version. That workflow needs no branch-protection exception.
- **`overlay.toml` at the repo root** holds the declared `protocol` and the ServUO compatibility
values, read by CI into the manifest. It exists because the number needs one maintained home —
see §7 for why the plugin cannot simply be asked.
The tarball uses a **fixed** top-level directory, `runicgateway-overlay/`, not a versioned one:
the installer looks for `overlay/`, `patches/` and `manifest.json` at known paths rather than
parsing the version it is trying to read. Member order, mtime and ownership are pinned, so a
given tree yields a byte-identical tarball and its checksum moves only when its contents do.
2. **`link`: make the sidecar installable.** Confirm/settle default data paths, and add a way to
read back config non-interactively (e.g. `--print-config` emitting JSON: bind addresses, token,
protocol version, db path) so the installer does not have to scrape logs for the token.
@@ -262,9 +292,9 @@ degrade loudly rather than silently.
```
✓ ServUO found /opt/ServUO (57.4)
✓ Overlay in sync 23 files, all hashes match install.json
✓ Overlay in sync 30 files, all hashes match install.json
⚠ Patch tier 1 of 3 applied — vendor.sale unavailable
✓ uo-link installed 3.0.1
✓ uo-link installed 0.3.0
✓ Service running, enabled
✓ Sidecar reachable 127.0.0.1:8080 /health ok
✓ Protocol sidecar 3 = overlay manifest 3
@@ -342,6 +372,41 @@ Three components version independently, bound by a protocol contract:
- **plugin overlay** — has no queryable version before ServUO boots. The overlay release
`manifest.json` declares it, and `install.json` records what was deployed.
### 7.0 The overlay manifest
Shipped inside every `runicgateway-overlay-<ver>.tar.gz`, generated by that repo's release workflow:
```json
{
"component": "servuo-plugins-overlay",
"version": "0.1.0",
"commit": "968b526…",
"repo": "RunicGateway/servuo-plugins",
"protocol": 3,
"servuo": { "min_version": "57.4", "patches_verified_against": "57.4" },
"files": { "overlay/Config/Bridge.cfg": "32718424…", "patches/…": "…" }
}
```
`version` and `commit` come from the release engine; `protocol` and the `servuo` block are read from
`servuo-plugins/overlay.toml`; `files` is a SHA256 per shipped file.
Two of these carry weight beyond documentation:
- **`protocol` is a hand-maintained declaration, and has to be.** The plugin announces no version on
the wire and none is queryable before ServUO boots, so nothing in CI can derive it — which makes
this line the only thing §7.1's gate 1 has to compare the sidecar against. The duty is stated in
`overlay.toml` and in that repo's README: **bump it in the same PR that changes the emitters**, the
way `link` bumps `PROTOCOL_VERSION`.
- **`files` is what makes `doctor` able to tell "the operator edited a deployed file" from "the
overlay moved on"** (§5, Phase 4). The installer copies these hashes into `install.json` at deploy
time; a later mismatch against *both* the manifest and `install.json` means upstream changed, a
mismatch against `install.json` alone means local edits.
`min_version` and `patches_verified_against` are separate on purpose. The base overlay only *adds*
files and is expected to work broadly; the patch tier diffs stock ServUO files and is verified
against exactly one version (§2.2).
### 7.1 The bundle manifest
**The bundle is the compat matrix.** Rather than the installer hardcoding versions or blindly
@@ -351,8 +416,8 @@ resolving "latest", CI publishes a small manifest naming an exact, checked combi
{
"bundle": "2026.08.01",
"protocol": 3,
"link": { "version": "3.0.1", "sha256": "a91f..." },
"overlay": { "version": "2.4.0", "commit": "a81f42c", "sha256": "7c3e..." }
"link": { "version": "0.3.0", "sha256": "a91f..." },
"overlay": { "version": "0.1.0", "commit": "968b526", "sha256": "7c3e..." }
}
```
@@ -372,7 +437,7 @@ Two gates run at compose time, both cheap and both worth it:
| Trigger | Why |
|---|---|
| `link` publishes a release | Its release job `POST`s to the installer repo's workflow-dispatch endpoint as its final step. `link/.gitea/workflows/release.yml` already declares `workflow_dispatch: {}` and already holds a `write:repository` token |
| `servuo-plugins` publishes a release | Same, once Phase 0 gives it a release workflow |
| `servuo-plugins` publishes a release | Same. Phase 0 item 1 gave it the release workflow; the dispatch step is marked as a TODO in that workflow's header and lands with the bundle CI it would call (item 3) — a step that `404`s on every release is worse than no step |
| Nightly cron on the installer repo | Recomputes from whatever the latest releases actually are, so a missed or failed dispatch self-heals instead of silently pinning operators to a stale sidecar |
`repository_dispatch` is deliberately avoided — support for it is uncertain on this Gitea version,
@@ -401,11 +466,12 @@ rather than being silently retriggered every night forever.
### 7.4 Open risk
The v3 cutover is mid-flight — protocol work landed on `edge` branches with the `edge → main`
cutover still open across four repos. Until that lands, `main` and `edge` disagree about
`PROTOCOL_VERSION`, so the installer must not hardcode a version anywhere; it reads what the
artifacts declare, and §7.1's gate 1 is what stops a mismatched pair from being published as a
bundle. See `docs/link/v3.md`.
**Settled as of the v3 cutover.** Protocol work landed on `edge` branches and the `edge → main`
cutover has now merged, so `main` speaks protocol 3 consistently across the repos. The rule it
motivated stands regardless and is not a temporary measure: **the installer hardcodes no protocol
version anywhere.** It reads what the artifacts declare, and §7.1's gate 1 is what stops a
mismatched pair from being published as a bundle — which is the mechanism that will matter at the
*next* protocol bump, not just this one. See `docs/link/v3.md`.
---
@@ -421,12 +487,14 @@ bundle. See `docs/link/v3.md`.
3. **Co-location assumption** — the shard dials out to the sidecar on loopback `127.0.0.1:7788`, so
sidecar and ServUO must share a host. Should the installer support installing only uo-link on a
different host, or hard-assume co-location?
4. **Branch targeting for the new repo**`link`, `website`, `servuo-plugins` and `docs` are
mid-cutover between `edge` and `main`. The installer repo starts clean on `main`; the Phase 0
`servuo-plugins` release workflow needs a target branch decision.
Resolved and moved into §1 / §2.2 / §5: uninstall scope, and minimum ServUO version.
**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
workflow targets `main`, and the installer repo starts clean on `main`. §7.4's caution still applies
in principle — the installer hardcodes no protocol version, it reads what the artifacts declare —
but the specific `edge`/`main` disagreement that motivated it is gone.
---
## 9. Administrator experience