All checks were successful
PR Checks / rust-gates (pull_request) Successful in 2m7s
Module-rust phase 18, step 5 of docs/modules/rust/PLAN.md §34.2.7 (D146, D148, D149, D153). Bundles: ServUO is read at schema 2 from v2/servuo/ and lowered into the schema-1 model. Schema 1 at the root is the fallback, so a pin from before schema 2 still reproduces. Rust bundles are read from v2/rust/. v2 reads use the contents API, because /raw/ is CDN-cached for six hours. --game rust runs install, update, doctor and uninstall for Rust servers (src/rustgame/): - the framework is detected from its marker files, which were read off both rigs; both or neither is refused; - --server-id names an instance: its own service (runicgateway-rust@<id>, or RunicGatewayRust-<id>), config, database and ports; - the plugin config is written once, with ServerId and Port only. An existing one is never rewritten, and one naming another server refuses the run; - each instance's sidecar.toml is written once with its ports and an absolute database path, and the sidecar generates the token into it; - one binary per host. update moves every instance, and a replaced binary restarts all of them; - doctor checks the plugin file hash, the plugin config's ServerId, the required uMod plugins (a warning), the service and /health, and passes when the plugin is connected; - uninstall removes our plugin and keeps its config. --purge also removes the sidecar config and database. The last instance takes the binary, the template and the record, and the shared user only when no ServUO record remains. service.rs takes the service name as a parameter internally. The ServUO public API is unchanged. Finding: Carbon 2.0.259's config.json has no folder keys, so carbon/plugins and carbon/configs are what the installer uses. The plan expected a moved directory to be readable there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
214 lines
12 KiB
Markdown
214 lines
12 KiB
Markdown
# Runic Gateway installer
|
|
|
|
A single-binary deployment tool that takes a **stock ServUO installation** and
|
|
configures it for Runic Gateway: deploys the shard plugin overlay, optionally
|
|
applies the stock-file patch tier, installs the uo-link sidecar and registers it
|
|
as a service, records what it deployed, and hands the operator the four values
|
|
that connect the website to the shard.
|
|
|
|
```
|
|
┌──────────────────────────────────────────┐
|
|
│ Runic Gateway installer (>>> HERE <<<)│
|
|
└───────────────┬──────────────────────────┘
|
|
│ deploys
|
|
┌───────────────┴────────────────┐
|
|
▼ ▼
|
|
ServUO integration uo-link sidecar
|
|
overlay sync + opt-in patch tier binary + config + service
|
|
(RunicGateway/servuo-plugins) (RunicGateway/link)
|
|
```
|
|
|
|
It is a **deployment tool, not a hosted bootstrapper** — no `curl | bash`, no
|
|
installer service. Artifacts are downloaded from a Gitea release page and run.
|
|
|
|
It also **does not replace ServUO startup behavior.** ServUO keeps running through
|
|
its existing release/start scripts; the installer never writes a launcher and
|
|
never restarts the shard.
|
|
|
|
## Install a shard with it
|
|
|
|
Grab a binary and `SHA256SUMS` from the
|
|
[releases page](https://gitea.whitlocktech.com/RunicGateway/installer/releases),
|
|
verify the checksum, and run it as Administrator/root against a **stopped** shard:
|
|
|
|
```bash
|
|
sha256sum -c SHA256SUMS --ignore-missing
|
|
chmod +x runicgateway-installer-linux-x86_64
|
|
sudo ./runicgateway-installer-linux-x86_64 install
|
|
```
|
|
|
|
```powershell
|
|
# Windows, from an elevated PowerShell
|
|
.\runicgateway-installer-windows-x86_64.exe install
|
|
```
|
|
|
|
It ends by printing the four values to paste into **Admin → Shard** on your site.
|
|
The full operator guide — what it asks, where it writes, the patch tier, day-two
|
|
commands and troubleshooting — is
|
|
[`installer/INSTALL.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/installer/INSTALL.md).
|
|
|
|
Prefer to place everything yourself, or on a host that cannot run the binary?
|
|
[INSTALL.md Appendix A](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/installer/INSTALL.md#appendix-a--installing-by-hand)
|
|
is the same deployment done with `curl`, `tar` and `systemctl`, and stays
|
|
supported.
|
|
|
|
## Status
|
|
|
|
**Released.** All five phases are built and the `edge → main` cutover (#17) cut
|
|
the first release, [`v0.1.0`](https://gitea.whitlocktech.com/RunicGateway/installer/releases),
|
|
publishing `linux-x86_64`, `linux-aarch64` and `windows-x86_64.exe` with
|
|
`SHA256SUMS`.
|
|
|
|
| Phase | State |
|
|
|---|---|
|
|
| 0 — prerequisites in the other repos | ✅ merged |
|
|
| 1 — installer core: bundle resolution, ServUO detection, overlay sync, `install.json` | ✅ released |
|
|
| 2 — uo-link install + service registration | ✅ released |
|
|
| 3 — the opt-in stock-file patch tier | ✅ released |
|
|
| 4 — `doctor`, `update`, `uninstall` | ✅ released |
|
|
| 5 — packaging polish: Linux `aarch64`, backup before overwrite | ✅ released |
|
|
|
|
The binary does everything
|
|
[`installer/INSTALL.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/installer/INSTALL.md)
|
|
describes: bundle resolution, ServUO detection and validation, the overlay sync,
|
|
the opt-in patch tier, `install.json`, the uo-link sidecar and its service, the
|
|
token handoff, and `doctor` / `update` / `uninstall`.
|
|
|
|
The design of record is
|
|
[`installer/PLAN.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/installer/PLAN.md)
|
|
in the docs repo: phases, locked decisions, and the Phase 0 prerequisites in other
|
|
repos (a `servuo-plugins` release workflow, a non-interactive config read-back in
|
|
`link`, and the bundle-manifest CI here), all of which have landed —
|
|
[`bundles/current.json`](https://gitea.whitlocktech.com/RunicGateway/installer/src/branch/bundles/current.json)
|
|
names the current protocol-checked sidecar + overlay combination, recomposed on
|
|
every component release and nightly (see [`bundles/README.md`](bundles/README.md)).
|
|
|
|
**`main` publishes.** `release.yml` cuts a release from every push to `main`, which
|
|
is why the crate was integrated on `edge` until it was worth handing to an
|
|
operator. Both cutover gates were met first: Phase 5 (its scope settled as **no
|
|
`.deb` and no MSI** — either would give the sidecar binary, its service unit and
|
|
its service account a second owner beside this tool), and the **Windows SCM half
|
|
verified on a real host**. That second one earned its place: `sc start` failed
|
|
with 1053 on its first real run and needed a sidecar fix (link#29) before it
|
|
passed 13/13.
|
|
|
|
## Related repos
|
|
|
|
| Repo | What |
|
|
|------|------|
|
|
| **this** — `RunicGateway/installer` | The installer (Rust, one binary per OS). |
|
|
| [RunicGateway/link](https://gitea.whitlocktech.com/RunicGateway/link) | The **uo-link sidecar** — the network-facing half of the game bridge. Installed and service-registered by this tool. |
|
|
| [RunicGateway/servuo-plugins](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins) | The **C# ServUO plugin** — deployed as source (`overlay/`) and compiled by ServUO at boot. Synced into the server tree by this tool. |
|
|
| [RunicGateway/website](https://gitea.whitlocktech.com/RunicGateway/website) | The public site and admin panel. The installer never contacts it — it prints values for Admin → Shard. |
|
|
| [RunicGateway/docs](https://gitea.whitlocktech.com/RunicGateway/docs) | All project documentation, including the installer plan. |
|
|
|
|
## Commands
|
|
|
|
| Command | What it does |
|
|
|---|---|
|
|
| `install` | Detect and validate the ServUO root, sync the overlay, optionally apply patches, install uo-link + service, write `install.json`, print the token handoff. |
|
|
| `doctor` | Diagnose an installed deployment end to end — through to *"has a shard actually dialed in?"*, the only check that distinguishes a working bridge from copied files. |
|
|
| `update` | Resolve the current bundle manifest, then update the sidecar (replace + restart) and the overlay (re-sync + tell the operator to restart ServUO). |
|
|
| `uninstall` | Remove only what the installer exclusively owns. It **never edits the ServUO tree** — it prints the overlay files to delete and the patch hunks to revert, and leaves that call to the operator. |
|
|
|
|
### Rust: `--game rust`
|
|
|
|
The same four verbs set up **Rust servers** (Oxide or Carbon) from a schema-2 Rust bundle — a
|
|
Rust-Link sidecar and a Rust-Plugins plugin, checked by CI to speak one protocol. A Rust host
|
|
commonly runs several servers, so each is a **named instance** with its own service, config,
|
|
database and ports; every instance shares one sidecar binary, and `update` moves them together.
|
|
|
|
```bash
|
|
sudo ./runicgateway-installer-linux-x86_64 install --game rust --rust /srv/rust --server-id main
|
|
```
|
|
|
|
- The framework is **detected**, never asked, and the plugin goes to `oxide/plugins/` or
|
|
`carbon/plugins/`. A tree with both, or neither, is refused.
|
|
- `--server-id` is the id the website knows the server by. Where the plugin has no config yet the
|
|
installer writes one holding just `ServerId` and `Port`; an existing one is never rewritten, and
|
|
one naming another server refuses the run.
|
|
- Services: `runicgateway-rust@<id>.service` (a systemd template) or `RunicGatewayRust-<id>`.
|
|
Records: `rust/install.json`, beside and separate from ServUO's.
|
|
- `doctor` checks the framework, the plugin file, the plugin config's `ServerId`, the required uMod
|
|
plugins (reported, never installed), the service, and `/health` through to *plugin connected*.
|
|
- `uninstall` removes the plugin file and keeps its config — that file is the website's.
|
|
|
|
A Pterodactyl panel uses the **egg** instead, published with each
|
|
[Rust-Link release](https://gitea.whitlocktech.com/RunicGateway/Rust-Link/releases). The Rust
|
|
operator guide is
|
|
[`rust-link/INSTALL.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/rust-link/INSTALL.md).
|
|
|
|
## Design constraints worth knowing up front
|
|
|
|
- **Releases are unsigned.** `SHA256SUMS` is the trust anchor; SmartScreen and
|
|
Gatekeeper warnings are expected and documented. The installer nonetheless
|
|
verifies the SHA256 of everything *it* downloads and refuses on mismatch.
|
|
- **Composition comes from a published bundle manifest**, not from "latest of
|
|
each". CI names an exact, protocol-checked combination of sidecar and overlay
|
|
versions; `--bundle <tag>` pins one for a reproducible install. A new component
|
|
release regenerates JSON, not this binary.
|
|
- **The base install must complete without the patch tier.** The patch tier edits
|
|
stock ServUO files, most real shards are hand-modified, and unverified ServUO
|
|
versions skip it with a warning rather than being patched blind.
|
|
- **A successful copy is not a working bridge.** ServUO ignores the script build's
|
|
exit code and silently reloads the previous `Scripts.dll`, so diagnostics verify
|
|
post-boot state rather than trusting a clean boot.
|
|
- **What a run overwrites is copied first.** Every `.cs` file the overlay owns is
|
|
replaced unconditionally, so an operator's edit to one is saved under
|
|
`backups/<timestamp>/` before it goes. Restoring is theirs to do — this tool
|
|
will not put an old file back over a newer release.
|
|
- **The audience is public** — any ServUO operator, not only shards we run.
|
|
|
|
## Build & run
|
|
|
|
A standard cargo project, with the crate at the repo root:
|
|
|
|
```bash
|
|
cargo build --release
|
|
cargo run -- --help
|
|
cargo run -- install --servuo /path/to/ServUO --verify # dry run: writes nothing
|
|
cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test
|
|
```
|
|
|
|
`RUNICGATEWAY_STATE_DIR` relocates **everything the installer writes** — state,
|
|
data, and the sidecar binary (normally `/etc/runicgateway`, `/var/lib/runicgateway`
|
|
and `/usr/bin`, or `%ProgramData%\RunicGateway` and `%ProgramFiles%\RunicGateway`).
|
|
It also suppresses service registration, since there is no such thing as a
|
|
relocated systemd unit or Windows service. That is how a full run is tested
|
|
without root.
|
|
|
|
Two layout notes that look odd until you know why:
|
|
|
|
- **The library target is `rgdeploy`, not `runicgateway_installer`.** Windows' UAC
|
|
installer detection refuses to launch an unsigned executable whose file name
|
|
contains `install` (`os error 740`), and Cargo names test harnesses after their
|
|
target — so a target under that name makes `cargo test` unrunnable on Windows.
|
|
The published binary keeps its documented name; `[[bin]] test = false` keeps
|
|
Cargo from building a harness under it. Expect a UAC prompt when running the
|
|
built binary on Windows; it needs Administrator anyway.
|
|
- **`Cargo.lock` is committed**, and CI builds `--locked`.
|
|
|
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development setup, the local
|
|
checks CI will run, and the branch/PR workflow.
|
|
|
|
---
|
|
|
|
## License
|
|
|
|
Runic Gateway is free software, licensed under the **GNU General Public License
|
|
v3.0 or later** — see [LICENSE.md](LICENSE.md).
|
|
|
|
Copyright (C) 2026 Runic Gateway
|
|
|
|
This program is free software: you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
Foundation, either version 3 of the License, or (at your option) any later
|
|
version. It is distributed WITHOUT ANY WARRANTY; without even the implied
|
|
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
Contributions are welcome — please read [CONTRIBUTING.md](CONTRIBUTING.md) (note
|
|
the **AI-usage disclosure** requirement) and our
|
|
[Code of Conduct](CODE_OF_CONDUCT.md). Report vulnerabilities privately per
|
|
[SECURITY.md](SECURITY.md).
|