Compare commits
7 Commits
04cd64b838
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8322e8318c | |||
| 25a5734107 | |||
| c583ddb77f | |||
| 29056ba996 | |||
| 186f057bc0 | |||
|
|
33a013ca98 | ||
| 5e2bc22a94 |
@@ -85,6 +85,7 @@ android-app/
|
||||
│ │ │ │ │ │ │ ├── PlayerShardDto.kt
|
||||
│ │ │ │ │ │ │ ├── PostDto.kt
|
||||
│ │ │ │ │ │ │ ├── PublicDto.kt
|
||||
│ │ │ │ │ │ │ ├── ShardContentDto.kt
|
||||
│ │ │ │ │ │ │ ├── ShardDto.kt
|
||||
│ │ │ │ │ │ │ ├── SsoDto.kt
|
||||
│ │ │ │ │ │ │ └── WikiDto.kt
|
||||
@@ -106,6 +107,7 @@ android-app/
|
||||
│ │ │ │ │ ├── NotificationsRepository.kt
|
||||
│ │ │ │ │ ├── PlayerShardRepository.kt
|
||||
│ │ │ │ │ ├── SettingsRepository.kt
|
||||
│ │ │ │ │ ├── ShardFeaturesRepository.kt
|
||||
│ │ │ │ │ ├── ShardRepository.kt
|
||||
│ │ │ │ │ └── WikiRepository.kt
|
||||
│ │ │ │ ├── di/
|
||||
@@ -171,6 +173,8 @@ android-app/
|
||||
│ │ │ │ │ ├── session/
|
||||
│ │ │ │ │ │ └── SessionViewModel.kt
|
||||
│ │ │ │ │ ├── shard/
|
||||
│ │ │ │ │ │ ├── AtlasScreen.kt
|
||||
│ │ │ │ │ │ ├── AtlasViewModel.kt
|
||||
│ │ │ │ │ │ ├── ChampsScreen.kt
|
||||
│ │ │ │ │ │ ├── ChampsViewModel.kt
|
||||
│ │ │ │ │ │ ├── FrameFields.kt
|
||||
@@ -180,7 +184,13 @@ android-app/
|
||||
│ │ │ │ │ │ ├── GuildsViewModel.kt
|
||||
│ │ │ │ │ │ ├── HousesScreen.kt
|
||||
│ │ │ │ │ │ ├── HousesViewModel.kt
|
||||
│ │ │ │ │ │ ├── LeaderboardsScreen.kt
|
||||
│ │ │ │ │ │ ├── LeaderboardsViewModel.kt
|
||||
│ │ │ │ │ │ ├── LiveBoard.kt
|
||||
│ │ │ │ │ │ ├── MarketScreen.kt
|
||||
│ │ │ │ │ │ ├── MarketViewModel.kt
|
||||
│ │ │ │ │ │ ├── RulesScreen.kt
|
||||
│ │ │ │ │ │ ├── RulesViewModel.kt
|
||||
│ │ │ │ │ │ ├── ShardComponents.kt
|
||||
│ │ │ │ │ │ ├── ShardEventText.kt
|
||||
│ │ │ │ │ │ ├── ShardScreen.kt
|
||||
@@ -284,6 +294,7 @@ android-app/
|
||||
│ │ │ │ │ ├── PlayerShardDtoTest.kt
|
||||
│ │ │ │ │ ├── PublicDtoTest.kt
|
||||
│ │ │ │ │ ├── ShardBoardDtoTest.kt
|
||||
│ │ │ │ │ ├── ShardContentDtoTest.kt
|
||||
│ │ │ │ │ ├── ShardDtoTest.kt
|
||||
│ │ │ │ │ ├── SsoDtoTest.kt
|
||||
│ │ │ │ │ └── WikiDtoTest.kt
|
||||
@@ -294,7 +305,8 @@ android-app/
|
||||
│ │ │ │ └── FakeShardStream.kt
|
||||
│ │ │ └── repository/
|
||||
│ │ │ ├── AccountTrustedDevicesTest.kt
|
||||
│ │ │ └── ConnectionVersionGuardTest.kt
|
||||
│ │ │ ├── ConnectionVersionGuardTest.kt
|
||||
│ │ │ └── ShardFeaturesRepositoryTest.kt
|
||||
│ │ ├── ui/
|
||||
│ │ │ ├── admin/
|
||||
│ │ │ │ ├── AdminContentViewModelTest.kt
|
||||
@@ -304,7 +316,8 @@ android-app/
|
||||
│ │ │ ├── contact/
|
||||
│ │ │ │ └── ContactViewModelTest.kt
|
||||
│ │ │ ├── navigation/
|
||||
│ │ │ │ └── MenuAccessTest.kt
|
||||
│ │ │ │ ├── MenuAccessTest.kt
|
||||
│ │ │ │ └── MenuFeatureGatingTest.kt
|
||||
│ │ │ ├── notifications/
|
||||
│ │ │ │ └── NotificationRoutingTest.kt
|
||||
│ │ │ ├── player/
|
||||
@@ -315,6 +328,8 @@ android-app/
|
||||
│ │ │ │ ├── FrameFieldsTest.kt
|
||||
│ │ │ │ ├── LiveBoardTest.kt
|
||||
│ │ │ │ ├── ShardBoardViewModelTest.kt
|
||||
│ │ │ │ ├── ShardContentHelpersTest.kt
|
||||
│ │ │ │ ├── ShardContentViewModelTest.kt
|
||||
│ │ │ │ └── ShardEventTextTest.kt
|
||||
│ │ │ ├── theme/
|
||||
│ │ │ │ └── BrandColorTest.kt
|
||||
|
||||
446
installer/PLAN.md
Normal file
446
installer/PLAN.md
Normal file
@@ -0,0 +1,446 @@
|
||||
# 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)).
|
||||
|
||||
---
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
Take a stock ServUO installation and configure it for Runic Gateway with minimal manual steps, while
|
||||
keeping the components separated and independently maintainable.
|
||||
|
||||
The installer handles environment detection, ServUO overlay deployment, the optional stock-file
|
||||
patch tier, uo-link installation and service registration, version tracking, diagnostics, and
|
||||
updates from Gitea releases.
|
||||
|
||||
**It is a deployment tool, not a hosted bootstrapper.** There is no `curl | bash`, no installer
|
||||
service, and no hosted bootstrap script. Artifacts are downloaded from a Gitea release page and run.
|
||||
|
||||
**It does not replace ServUO startup behavior.** ServUO keeps running through its existing
|
||||
release/start scripts. The installer never writes a launcher.
|
||||
|
||||
### Decisions locked
|
||||
|
||||
| Question | Decision |
|
||||
|---|---|
|
||||
| Audience | **Public** — any ServUO operator, not just shards we run |
|
||||
| Code signing | **Unsigned.** `SHA256SUMS` is the trust anchor; SmartScreen/Gatekeeper warnings are expected and documented, as with most self-hosted tooling |
|
||||
| Language | **Rust** — single static binary per OS, reuses the cross-compile pattern already proven in `link/.gitea/workflows/release.yml` |
|
||||
| Plugin source | **Release tarball artifact** — no git and no Gitea credentials on the shard host |
|
||||
| Composition | **Published bundle manifest** (§7.1). CI names an exact, protocol-checked combination of component versions; the installer fetches it at run time and `--bundle <tag>` pins one. Component releases regenerate JSON, not the installer binary |
|
||||
| Token handoff | **Print token + prefilled admin URL** at the end of the run |
|
||||
| Repo | **New repo**, `RunicGateway/installer`. It deploys *both* other components, so living inside `link/` would invert the dependency |
|
||||
| ServUO version | **Warn and skip.** Patches are verified against stock 57.4 only; on anything else the base install proceeds and the patch tier is skipped with a warning. Forks are the norm in a public audience — refusing outright would block most operators |
|
||||
| Uninstall | **Never touches the ServUO tree.** Removes uo-link and its service entry, then *prints* the overlay files to delete and the patch hunks to revert. Reverting is the operator's call |
|
||||
|
||||
---
|
||||
|
||||
## 2. Corrections to the original overview
|
||||
|
||||
These are not wording nits — each one changes what the installer has to do.
|
||||
|
||||
### 2.1 There is no `RunicGateway.dll` and no `Plugins/` directory
|
||||
|
||||
The plugin ships as **C# source** and ServUO compiles it at boot. The real deployable is
|
||||
`servuo-plugins/overlay/`, which mirrors the server root:
|
||||
|
||||
```
|
||||
overlay/
|
||||
├── Config/Bridge.cfg
|
||||
└── Scripts/
|
||||
├── Scripts.csproj # Phase 0 — whole-file overwrite of a stock file
|
||||
└── Custom/Bridge/*.cs # 22 files
|
||||
```
|
||||
|
||||
So the plugin step is a hash-compare file sync, not a DLL drop — mechanically easier than the
|
||||
overview assumed. The sting is that **a successful copy does not mean a working bridge.** Per
|
||||
`link/SHARD_PREREQS.md`, `ScriptCompiler.Compile()` shells out to `dotnet build`, prints the output,
|
||||
**ignores the exit code**, and reloads the existing `Scripts.dll`. A broken script build is
|
||||
invisible: the shard boots clean on stale code. Diagnostics must therefore verify *post-boot* state,
|
||||
never treat "files copied" as success.
|
||||
|
||||
### 2.2 Stock ServUO files *are* modified — by an optional tier
|
||||
|
||||
`servuo-plugins/patches/` holds unified diffs against stock ServUO 57.4, plus two `.cs` files that
|
||||
can only be copied *after* their patch lands (they reference symbols the patch introduces):
|
||||
|
||||
| Patch | Target | Companion file | Rebuild required |
|
||||
|---|---|---|---|
|
||||
| `playervendor-sale-eventsink.patch` | `Server/EventSink.cs` | `BridgeVendorSale.cs` | **Core** — `dotnet build ServUO.sln`; the dynamic script build is not enough |
|
||||
| `playervendor-sale-gump.patch` | `Scripts/Gumps/PlayerVendorGumps.cs` | (same unit as above) | script build |
|
||||
| `commandlogging-event.patch` | `Scripts/Commands/Logging.cs` | `BridgeModerationAudit.cs` | script build |
|
||||
|
||||
Plus `overlay/Scripts/Scripts.csproj`, which overwrites a stock file (Phase 0 — it fixes the silent
|
||||
ServUO build bug above).
|
||||
|
||||
This is the hardest part of the installer. `git apply` against a hand-modified shard will fail, and
|
||||
most real shards are hand-modified. Therefore:
|
||||
|
||||
- The patch tier is **opt-in and skippable**. The base install must complete without it.
|
||||
- Always dry-run (`git apply --check`) before applying, and report per-patch.
|
||||
- When skipped or failed, say plainly what is lost: **no `vendor.sale` events, no in-game moderation
|
||||
audit forwarding**.
|
||||
- The `EventSink.cs` patch must warn loudly that a **core solution rebuild** is required, not just a
|
||||
shard restart.
|
||||
- On any ServUO version other than stock **57.4**, skip the whole tier with a warning and continue
|
||||
with the base install. Do not attempt to apply unverified diffs to an unknown tree.
|
||||
- Record applied patches in `install.json`, **and cache the applied `.patch` files** next to it
|
||||
(`/etc/runicgateway/patches/`, `%ProgramData%\RunicGateway\patches\`). Re-runs stay idempotent,
|
||||
and uninstall can print the exact hunks offline long after the release tarball is gone (§5,
|
||||
Phase 4).
|
||||
|
||||
### 2.3 Config paths collide with what the sidecar actually reads
|
||||
|
||||
The sidecar reads `$UOLINK_CONFIG`, else `sidecar.toml` in the **working directory**
|
||||
(`link/sidecar/src/config.rs`), with keys `[shard].bind`, `[web].bind`, `[web].auth_token`,
|
||||
`[store].path`. The overview proposed a `config.toml` with `[updates]`, `[link]`, `[servuo]` — keys
|
||||
the sidecar cannot read.
|
||||
|
||||
Two files, two owners:
|
||||
|
||||
| File | Owner | Contents |
|
||||
|---|---|---|
|
||||
| `/etc/runicgateway/sidecar.toml` | uo-link | The sidecar's own schema, unchanged. Service sets `UOLINK_CONFIG` to this path |
|
||||
| `/etc/runicgateway/install.json` | installer | Deployed versions, file hashes, applied patches, ServUO path, timestamps |
|
||||
|
||||
**Working-directory trap:** the sidecar writes both `sidecar.toml` and `uo-link.db` relative to CWD.
|
||||
Under `C:\Program Files\` that fails or silently lands in VirtualStore. The service definitions must
|
||||
pin `UOLINK_CONFIG` and `UOLINK_DB_PATH` explicitly:
|
||||
|
||||
- Linux: config `/etc/runicgateway/sidecar.toml`, db `/var/lib/runicgateway/uo-link.db`, dedicated
|
||||
service user
|
||||
- Windows: binary under `%ProgramFiles%\RunicGateway\`, **data under `%ProgramData%\RunicGateway\`**
|
||||
|
||||
### 2.4 The token handoff was missing entirely
|
||||
|
||||
The whole point is the website reaching the sidecar, and today that is manual and undocumented in
|
||||
the install flow: the sidecar generates a token on first run and logs it, then a human pastes base
|
||||
URL, WS URL, token, and protocol version into Admin → Shard, where it is AES-GCM encrypted and
|
||||
becomes write-only. This is the largest "I installed it and nothing happened" failure mode.
|
||||
|
||||
The installer closes it by printing a copy-paste block at the end of a successful run — see §6.
|
||||
|
||||
### 2.5 `deploy.ps1` cannot be the cross-platform deployer
|
||||
|
||||
It is PowerShell-only; a Linux ServUO host running .NET typically has no `pwsh`. It also hard-throws
|
||||
when the ServUO process is running — correct behavior, and the installer must inherit it (detect and
|
||||
refuse, rather than corrupt a live `Scripts.dll`). The installer reimplements the sync natively; it
|
||||
is a short hash-compare-and-copy that never deletes.
|
||||
|
||||
`deploy.ps1` **stays** in `servuo-plugins` as the developer-facing tool. The installer is for
|
||||
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).
|
||||
- **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.
|
||||
|
||||
---
|
||||
|
||||
## 3. Distribution model
|
||||
|
||||
Components are published as Gitea release artifacts. Operators download from the release page
|
||||
(browser, `curl`/`wget`, or `scp` to the server) and run the binary.
|
||||
|
||||
```
|
||||
Runic Gateway Installer v1.0.0
|
||||
├── runicgateway-installer-windows-x86_64.exe
|
||||
├── runicgateway-installer-linux-x86_64
|
||||
└── SHA256SUMS
|
||||
|
||||
uo-link v3.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)
|
||||
└── SHA256SUMS
|
||||
|
||||
servuo-plugins v<ver> (new release, Phase 0)
|
||||
├── runicgateway-overlay-<ver>.tar.gz # overlay/ + patches/ + manifest.json
|
||||
└── SHA256SUMS
|
||||
```
|
||||
|
||||
Binding those together is the **bundle manifest** (§7.1) — published by the installer repo's CI, not
|
||||
by any component, and the thing the installer actually resolves against.
|
||||
|
||||
```bash
|
||||
scp runicgateway-installer-linux-x86_64 user@server:/tmp/
|
||||
chmod +x runicgateway-installer-linux-x86_64
|
||||
sudo ./runicgateway-installer-linux-x86_64
|
||||
```
|
||||
|
||||
### Unsigned-binary posture
|
||||
|
||||
Because releases are unsigned, trust is anchored on checksums and the operator's own verification.
|
||||
The docs must state this up front rather than let users discover it as a scary dialog:
|
||||
|
||||
- Every release publishes `SHA256SUMS`; the install docs lead with the verification command for both
|
||||
OSes.
|
||||
- Windows will show a SmartScreen "unrecognized app" prompt. Documented, with the exact click path.
|
||||
- The installer verifies the SHA256 of everything **it** downloads (overlay tarball, sidecar binary)
|
||||
against the release's `SHA256SUMS` and refuses on mismatch. Self-verification is not optional just
|
||||
because the installer itself is unsigned.
|
||||
- Revisit signing if it ever becomes affordable; the release layout should not have to change.
|
||||
|
||||
---
|
||||
|
||||
## 4. Component architecture
|
||||
|
||||
```
|
||||
Runic Gateway Installer (Rust, one binary per OS)
|
||||
│
|
||||
┌───────────────┴────────────────┐
|
||||
▼ ▼
|
||||
ServUO integration uo-link
|
||||
│ │
|
||||
┌────────┴────────┐ ┌────────┴────────┐
|
||||
▼ ▼ ▼ ▼
|
||||
overlay sync patch tier (opt-in) binary install service registration
|
||||
(never deletes) (git apply + guard) + config + data (systemd / Windows SCM)
|
||||
```
|
||||
|
||||
Each component keeps its own lifecycle. ServUO's existing startup process is untouched.
|
||||
|
||||
---
|
||||
|
||||
## 5. Phases
|
||||
|
||||
### Phase 0 — prerequisites (no installer code)
|
||||
|
||||
Repo work that must land before an installer can exist.
|
||||
|
||||
1. **`servuo-plugins`: add `.gitea/workflows/release.yml`.** Retarget the release *engine* half of
|
||||
`link/release.yml` (its header comment explicitly anticipates this — the plan/release steps
|
||||
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).
|
||||
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.
|
||||
3. **Bundle CI in the installer repo** (§7). Compose job (read both repos' latest releases → run the
|
||||
two gates → publish `bundle.json`), the nightly cron, and the dispatch step appended to each
|
||||
component's release workflow. This must exist before Phase 1 is useful, since the installer
|
||||
resolves what to install *from* the bundle.
|
||||
4. **`docs`: this file, plus `docs/installer/INSTALL.md`** (the operator-facing guide) once the
|
||||
shape is settled.
|
||||
|
||||
### Phase 1 — installer core
|
||||
|
||||
- ServUO root detection and validation (`ServUO.exe`, `Scripts/`, `Config/`), with version detection
|
||||
and an explicit refusal when the ServUO process is running.
|
||||
- Overlay sync: fetch tarball → verify SHA256 → hash-compare against the server tree → add/change,
|
||||
**never delete**. Port of `deploy.ps1` semantics including its `-Verify` dry run (`--verify`).
|
||||
- Write `install.json`: component, version, source commit, per-file hashes, applied patches,
|
||||
timestamp.
|
||||
- Idempotent re-runs; a second run with no upstream change reports "unchanged" and writes nothing.
|
||||
|
||||
### Phase 2 — uo-link install and service
|
||||
|
||||
- Linux: binary → `/usr/bin/runicgateway-link`, config → `/etc/runicgateway/sidecar.toml`, db →
|
||||
`/var/lib/runicgateway/`, systemd unit with a dedicated user, `enable` + `start`.
|
||||
- Windows: `%ProgramFiles%\RunicGateway\`, data in `%ProgramData%\RunicGateway\`, service
|
||||
registration with automatic start and restart-on-failure.
|
||||
- Both: `UOLINK_CONFIG` and `UOLINK_DB_PATH` pinned in the service definition (§2.3).
|
||||
- Token surfacing (§6).
|
||||
|
||||
### Phase 3 — patch tier (opt-in)
|
||||
|
||||
Everything in §2.2. Detect applicability, dry-run, apply, record, warn about the core rebuild, and
|
||||
degrade loudly rather than silently.
|
||||
|
||||
### Phase 4 — diagnostics and updates
|
||||
|
||||
`runicgateway doctor` — the command that makes the whole thing supportable:
|
||||
|
||||
```
|
||||
✓ ServUO found /opt/ServUO (57.4)
|
||||
✓ Overlay in sync 23 files, all hashes match install.json
|
||||
⚠ Patch tier 1 of 3 applied — vendor.sale unavailable
|
||||
✓ uo-link installed 3.0.1
|
||||
✓ Service running, enabled
|
||||
✓ Sidecar reachable 127.0.0.1:8080 /health ok
|
||||
✓ Protocol sidecar 3 = overlay manifest 3
|
||||
✗ Shard connected no shard has dialed in since boot
|
||||
```
|
||||
|
||||
The last check matters most: it is the only thing that distinguishes "files copied" from "the bridge
|
||||
actually works" (§2.1).
|
||||
|
||||
`runicgateway update` — resolves the current bundle (§7.1), then acts asymmetrically by component,
|
||||
deliberately:
|
||||
|
||||
- **uo-link**: compare the bundle's version against what is installed → download → verify checksum →
|
||||
replace binary → restart service.
|
||||
- **plugin overlay**: download the bundle's overlay tarball → verify → re-sync → record commit →
|
||||
tell the operator ServUO must restart (the installer does not restart the shard).
|
||||
|
||||
Because both come from one bundle, an update always moves to a combination whose protocol versions
|
||||
were checked together, rather than to two independently-latest artifacts that may disagree.
|
||||
|
||||
`runicgateway uninstall` — **removes only what it exclusively owns, and never edits the ServUO
|
||||
tree.** The installer cannot know what the operator has changed in those files since deployment, so
|
||||
a clever automatic revert risks silently eating their work. It removes and it reports:
|
||||
|
||||
| Action | Scope |
|
||||
|---|---|
|
||||
| Removed | uo-link binary, its service entry (systemd unit / Windows service), `install.json` and the cached patch set |
|
||||
| Kept | `sidecar.toml` and `uo-link.db` (config and history survive; `--purge` to drop them) |
|
||||
| **Printed, not done** | Every overlay file deployed into the ServUO tree, listed by path, for the operator to delete |
|
||||
| **Printed, not done** | The exact hunks each applied patch added to `EventSink.cs`, `PlayerVendorGumps.cs`, `Logging.cs`, rendered from the cached `.patch` files, for the operator to revert by hand |
|
||||
|
||||
The printed report is also written to a file, so it survives the terminal scrollback of a long
|
||||
uninstall.
|
||||
|
||||
### Phase 5 — packaging polish
|
||||
|
||||
`.deb` packaging, Windows MSI, arm64 cross build, and optional automated backup before upgrade.
|
||||
Deliberately last: v1 can register services directly (`sc create` / a written systemd unit) and ship
|
||||
plain binaries. Nothing in Phases 1–4 should have to change to add these.
|
||||
|
||||
---
|
||||
|
||||
## 6. Token handoff (the end of a successful run)
|
||||
|
||||
```
|
||||
Runic Gateway is installed.
|
||||
|
||||
One manual step remains — connect the website to this sidecar:
|
||||
|
||||
Base URL http://<this-host>:8080
|
||||
WebSocket URL ws://<this-host>:8080/ws
|
||||
Protocol version 3
|
||||
Auth token 4f9c... (also in /etc/runicgateway/sidecar.toml)
|
||||
|
||||
Paste these into Admin → Shard on your Runic Gateway site:
|
||||
https://<your-site>/admin/shard
|
||||
|
||||
The token is write-only once saved — the site will never show it back to you.
|
||||
```
|
||||
|
||||
The installer prompts for the site URL only to build that link; it never contacts the website. A
|
||||
future "installer registers itself with the website" flow (claim code + authenticated endpoint) is
|
||||
explicitly **out of scope** — it is real backend work in a security-sensitive area and can be added
|
||||
later without changing anything here.
|
||||
|
||||
---
|
||||
|
||||
## 7. Version tracking, the bundle, and release orchestration
|
||||
|
||||
Three components version independently, bound by a protocol contract:
|
||||
|
||||
- **sidecar** — `PROTOCOL_VERSION` in `link/sidecar/src/main.rs`, exposed on `/health` and as
|
||||
`X-UOLink-Version` on every response; a mismatch is rejected `409`.
|
||||
- **website** — stores an expected protocol version in `uoLinkConfig` (admin-managed).
|
||||
- **plugin overlay** — has no queryable version before ServUO boots. The overlay release
|
||||
`manifest.json` declares it, and `install.json` records what was deployed.
|
||||
|
||||
### 7.1 The bundle manifest
|
||||
|
||||
**The bundle is the compat matrix.** Rather than the installer hardcoding versions or blindly
|
||||
resolving "latest", CI publishes a small manifest naming an exact, checked combination:
|
||||
|
||||
```json
|
||||
{
|
||||
"bundle": "2026.08.01",
|
||||
"protocol": 3,
|
||||
"link": { "version": "3.0.1", "sha256": "a91f..." },
|
||||
"overlay": { "version": "2.4.0", "commit": "a81f42c", "sha256": "7c3e..." }
|
||||
}
|
||||
```
|
||||
|
||||
The installer fetches the current bundle at run time; `--bundle <tag>` pins an older one for a
|
||||
reproducible install. Because the bundle is data, **a new `link` release regenerates ~20 lines of
|
||||
JSON and leaves the installer binary untouched** — operators do not re-download the installer to
|
||||
pick up a sidecar patch, and the installer does not accumulate releases whose code is byte-identical.
|
||||
|
||||
Two gates run at compose time, both cheap and both worth it:
|
||||
|
||||
1. The sidecar's `PROTOCOL_VERSION` must equal the overlay manifest's declared protocol version.
|
||||
This is the check that catches an `edge`/`main` protocol mismatch before it reaches an operator.
|
||||
2. Every referenced asset must exist and its SHA256 must match the publishing repo's `SHA256SUMS`.
|
||||
|
||||
### 7.2 What triggers a bundle
|
||||
|
||||
| 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 |
|
||||
| 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,
|
||||
whereas dispatching an existing `workflow_dispatch` workflow via the API works today.
|
||||
|
||||
### 7.3 Stale-overlay handling: dispatch, don't wait
|
||||
|
||||
Each component **self-releases on merge to its own `main`**, using the same conventional-commit
|
||||
engine. Note that "updated since the last release" must mean *releasable* commits — the engine sets
|
||||
`RELEASE=false` when nothing but `docs:`/`chore:` has landed, so a docs typo correctly does **not**
|
||||
cut an overlay release, and the bundle keeps using the existing one.
|
||||
|
||||
So by the time the installer's CI looks, the release normally already exists. If it finds
|
||||
`servuo-plugins` main ahead of its latest release *with* releasable commits, it:
|
||||
|
||||
1. fires that repo's release workflow via workflow-dispatch and **does not wait for it**,
|
||||
2. composes this bundle from the assets that exist right now,
|
||||
3. writes a loud warning into the job summary.
|
||||
|
||||
The new overlay release lands minutes later on its own and the nightly cron folds it into the next
|
||||
bundle. This gets the automation without the flaky part: dispatching another repo's workflow is
|
||||
fine — that workflow still runs its own gates — but *polling* it is not, because Gitea's dispatch
|
||||
endpoint returns no run handle, so the job would have to guess which run is its own and hold a
|
||||
runner idle meanwhile. The warning exists so a genuinely broken release workflow surfaces once
|
||||
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`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Open questions
|
||||
|
||||
1. **Windows service mechanism** — `sc create` against the plain console binary (simplest, works
|
||||
today), a bundled WinSW/NSSM shim, or a native `--service` mode in the sidecar using the
|
||||
`windows-service` crate (cleanest, but changes `link`). Recommendation: `sc create` for v1,
|
||||
revisit if restart semantics prove inadequate.
|
||||
2. **Does the installer manage ServUO stop/start?** Currently it refuses while ServUO runs and tells
|
||||
the operator to restart afterward. Offering to stop/start would be friendlier but means owning
|
||||
another shard's process lifecycle, and the shard's own start scripts vary.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## 9. Administrator experience
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
find plugins → copy files → edit ServUO → download bridge → start bridge
|
||||
→ configure startup → find the token → troubleshoot paths
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
download artifact → verify checksum → run installer → select ServUO directory
|
||||
→ install components → paste 4 values into Admin → Shard → start ServUO normally
|
||||
```
|
||||
Reference in New Issue
Block a user