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>
875 lines
48 KiB
Markdown
875 lines
48 KiB
Markdown
# Installing Runic Gateway on your shard
|
||
|
||
Operator guide for the **Runic Gateway installer** — the tool that takes a working ServUO
|
||
installation and connects it to a Runic Gateway website.
|
||
|
||
> **Status: the installer binary is not released yet.**
|
||
>
|
||
> Phases 1 to 3 are built and live on the installer repo's `edge` branch: the installer core
|
||
> (bundle resolution, ServUO detection, the overlay sync, `install.json`), the sidecar half (the
|
||
> binary, its config, its service, and the token handoff), and the
|
||
> [patch tier](#4-the-patch-tier-optional). `install` is therefore complete; what is still missing
|
||
> is [`doctor`, `update` and `uninstall`](#7-day-two), each of which reports which phase it arrives
|
||
> in rather than failing as though you had mistyped it. The first release is being cut from that
|
||
> branch now.
|
||
>
|
||
> Everything it installs *is* released and published — the sidecar, the plugin overlay, and the
|
||
> [bundle manifest](https://gitea.whitlocktech.com/RunicGateway/installer/src/branch/main/bundles/current.json)
|
||
> that names the checked combination of the two. This guide is the operator-facing contract those
|
||
> phases build to, and it is written first on purpose: it is the specification of what the run
|
||
> looks like, what it asks, where it writes, and what it prints.
|
||
>
|
||
> **You can install today without it** — [Appendix A](#appendix-a--installing-by-hand) is the same
|
||
> deployment done by hand, with the commands verified against the current releases. When the binary
|
||
> ships, Appendix A stays as the reference for what it does under the hood.
|
||
>
|
||
> Design of record: [PLAN.md](PLAN.md).
|
||
|
||
---
|
||
|
||
## What this installs
|
||
|
||
Three things, on the machine that runs your shard:
|
||
|
||
| # | Component | Where it comes from |
|
||
|---|---|---|
|
||
| 1 | **The plugin overlay** — C# source that ServUO compiles at boot, copied into your server tree | [`RunicGateway/servuo-plugins`](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins) release tarball |
|
||
| 2 | **The uo-link sidecar** — a small Rust service that the shard dials out to, and that your website reads from | [`RunicGateway/link`](https://gitea.whitlocktech.com/RunicGateway/link) release binary |
|
||
| 3 | **A record of what it did** — `install.json`, plus cached copies of the patches and of every file the patch tier edited | Written by the installer |
|
||
|
||
```
|
||
ServUO shard ──loopback TCP 127.0.0.1:7788──► uo-link sidecar ──HTTP + WebSocket──► website
|
||
(1) overlay (2) binary + service (yours)
|
||
```
|
||
|
||
The shard **dials out**; it never listens for the website and is never reachable from the internet.
|
||
Only the sidecar is exposed, and only to your website.
|
||
|
||
### What it deliberately does not do
|
||
|
||
- **It never restarts or manages ServUO.** Your shard keeps starting the way it always has. The
|
||
installer refuses to run while ServUO is up, and tells you when a restart is required.
|
||
- **It never deletes anything from your server tree.** The overlay sync only adds and overwrites.
|
||
- **It never contacts your website.** It prints four values for you to paste into Admin → Shard.
|
||
- **It never edits stock ServUO files without asking.** That is the opt-in
|
||
[patch tier](#4-the-patch-tier-optional), and skipping it still leaves you with a working bridge.
|
||
|
||
---
|
||
|
||
## Before you begin
|
||
|
||
| Requirement | Detail |
|
||
|---|---|
|
||
| A working ServUO install | It must currently boot and compile scripts cleanly. The installer deploys onto a healthy shard; it does not repair a broken one. |
|
||
| ServUO **57.4** *(patch tier only)* | **57.4 is the only supported version.** The base install works on any reasonably current ServUO. The patch tier is written and tested against stock 57.4; on any other version it is **unsupported and untested** — you can still choose to run it, behind an explicit opt-in, and it applies only where the exact lines it patches are unchanged. See [§4](#4-the-patch-tier-optional). |
|
||
| ServUO **stopped** | `ServUO.exe` holds a lock on `Scripts.dll` and writes `Saves/` on exit. The installer refuses to deploy under a running shard. |
|
||
| Administrator / root | It writes into system directories and registers a service. |
|
||
| Outbound HTTPS | To `gitea.whitlocktech.com`, to fetch the bundle and the two artifacts. Nothing inbound is needed, and no Gitea account or git client is required. |
|
||
| The sidecar on the **same host** as the shard | The shard connects to `127.0.0.1:7788`. Splitting them is not supported — the loopback socket *is* the trust boundary for inbound commands. |
|
||
| Admin access to your Runic Gateway site | The last step is pasting four values into Admin → Shard. |
|
||
|
||
**Back up first.** The overlay overwrites `Scripts/Scripts.csproj` (a stock file), and the patch
|
||
tier edits stock sources. A copy of `Scripts/` and `Config/` before you start costs nothing.
|
||
|
||
---
|
||
|
||
## 1. Download and verify
|
||
|
||
Releases are **unsigned**. There is no code-signing certificate and no notarization, so the
|
||
`SHA256SUMS` file published beside every artifact is the whole trust anchor — check it.
|
||
|
||
Download the installer for your OS, plus `SHA256SUMS`, from the
|
||
[installer releases page](https://gitea.whitlocktech.com/RunicGateway/installer/releases):
|
||
|
||
```
|
||
runicgateway-installer-linux-x86_64
|
||
runicgateway-installer-windows-x86_64.exe
|
||
SHA256SUMS
|
||
```
|
||
|
||
**Linux**
|
||
|
||
```bash
|
||
sha256sum -c SHA256SUMS --ignore-missing
|
||
chmod +x runicgateway-installer-linux-x86_64
|
||
```
|
||
|
||
**Windows** (PowerShell)
|
||
|
||
```powershell
|
||
(Get-FileHash .\runicgateway-installer-windows-x86_64.exe -Algorithm SHA256).Hash
|
||
Get-Content .\SHA256SUMS # compare the line for this file, case-insensitively
|
||
```
|
||
|
||
Windows will show a **SmartScreen "Windows protected your PC"** prompt on first run, because the
|
||
binary is unsigned and unknown. Once you have verified the checksum above: *More info* →
|
||
*Run anyway*. If you would rather not, Appendix A's manual path uses no unsigned binary except the
|
||
sidecar itself, which you verify the same way.
|
||
|
||
The installer applies the same standard to everything **it** downloads: each artifact's SHA256 is
|
||
checked against the value recorded in the bundle manifest — which CI computed after verifying it
|
||
against the publishing repo's own `SHA256SUMS` — and a mismatch aborts the run.
|
||
|
||
### What it installs is a bundle, not "latest"
|
||
|
||
The three components version independently but must agree on one wire protocol, so CI publishes a
|
||
[**bundle**](https://gitea.whitlocktech.com/RunicGateway/installer/src/branch/main/bundles/README.md):
|
||
one exact, protocol-checked pair of sidecar + overlay versions. The installer resolves that at run
|
||
time rather than hardcoding versions or blindly taking each repo's newest release.
|
||
|
||
Consequences worth knowing:
|
||
|
||
- A sidecar patch release does **not** mean re-downloading the installer. The bundle is data.
|
||
- `--bundle <tag>` (e.g. `--bundle 2026.08.04`) pins an exact past combination, so a reinstall six
|
||
months from now reproduces today's install rather than tomorrow's.
|
||
|
||
---
|
||
|
||
## 2. Run it
|
||
|
||
```bash
|
||
sudo ./runicgateway-installer-linux-x86_64 install
|
||
```
|
||
|
||
```powershell
|
||
# Windows: from an elevated PowerShell
|
||
.\runicgateway-installer-windows-x86_64.exe install
|
||
```
|
||
|
||
Run `install --verify` first if you want to see exactly what would change and write nothing — the
|
||
same idea as `deploy.ps1 -Verify`, which developers of the plugin use.
|
||
|
||
The installer **does not install itself.** Keep the binary somewhere sensible on the host (it is
|
||
one file); `doctor`, `update` and `uninstall` are run from it later. Examples below shorten it to
|
||
`runicgateway`.
|
||
|
||
### What it asks
|
||
|
||
1. **Your ServUO root** — detected if the installer is run from inside it or from an obvious
|
||
sibling, otherwise prompted. A directory qualifies only if it contains `ServUO.exe`, `Scripts/`
|
||
and `Config/`.
|
||
2. **Whether to apply the patch tier** — off unless you say yes. On a ServUO that is not 57.4 the
|
||
prompt defaults to **no** and carries an unsupported-version warning you have to answer past.
|
||
See [§4](#4-the-patch-tier-optional).
|
||
3. **The hostname your website should use to reach this machine** — used only to compose the two
|
||
URLs it prints at the end. The sidecar's bind address is frequently `127.0.0.1` or `0.0.0.0`,
|
||
neither of which is something to hand to a website.
|
||
4. **Your site's URL** — used only to print a clickable link to its Admin → Shard page. The
|
||
installer never contacts your website.
|
||
|
||
### An illustrative run
|
||
|
||
```
|
||
Runic Gateway installer — bundle 2026.08.04 (protocol 3)
|
||
|
||
ServUO /opt/ServUO (57.4)
|
||
Shard process not running
|
||
Overlay servuo-plugins v0.1.1 protocol 3
|
||
Sidecar uo-link v1.1.0 protocol 3
|
||
|
||
✓ overlay tarball verified sha256 75dc6d6c…
|
||
✓ sidecar binary verified sha256 27d491ef…
|
||
|
||
Overlay sync
|
||
ADD Config/Bridge.cfg
|
||
ADD Scripts/Custom/Bridge/*.cs (22 files)
|
||
CHANGE Scripts/Scripts.csproj
|
||
deployed. add=23 change=1 unchanged=0 kept=0
|
||
|
||
Patch tier not selected
|
||
Without it: no vendor.sale events, no in-game moderation audit forwarding.
|
||
|
||
uo-link sidecar
|
||
binary /usr/bin/runicgateway-link install
|
||
✓ sidecar binary verified sha256 27d491ef…
|
||
config /etc/runicgateway/sidecar.toml created
|
||
database /var/lib/runicgateway/uo-link.db
|
||
listening on shard 127.0.0.1:7788 website 127.0.0.1:8080
|
||
service runicgateway-link.service active, enabled
|
||
running as runicgateway
|
||
|
||
Recorded /etc/runicgateway/install.json
|
||
|
||
Scripts.csproj changed — ServUO rebuilds Scripts.dll on next boot.
|
||
Start your shard when ready; the installer does not start it for you.
|
||
```
|
||
|
||
Then the [token handoff](#5-connect-the-website).
|
||
|
||
### Commands and flags
|
||
|
||
The surface this guide specifies. Each command is idempotent: a second run with nothing new to do
|
||
reports "unchanged" and writes nothing.
|
||
|
||
| Command | What it does |
|
||
|---|---|
|
||
| `install` | The full run above. |
|
||
| `doctor` | Diagnoses an existing deployment end to end — see [§7](#7-day-two). |
|
||
| `update` | Re-resolves the bundle; updates the sidecar (replace + restart) and the overlay (re-sync + tell you to restart ServUO). |
|
||
| `uninstall` | Removes only what the installer exclusively owns; prints — never performs — anything inside your ServUO tree. |
|
||
|
||
| Flag | Applies to | Meaning |
|
||
|---|---|---|
|
||
| `--verify` | `install`, `update` | Dry run. Report every change that would be made; write nothing. |
|
||
| `--servuo <path>` | `install`, `doctor`, `update` | Name the ServUO root instead of detecting or prompting. |
|
||
| `--bundle <tag>` | `install`, `update` | Pin an exact published bundle instead of the current one. |
|
||
| `--patches` / `--no-patches` | `install`, `update` | Decide the patch tier non-interactively. `--patches` never loosens the region check: patches whose target lines are not stock are reported for you to apply by hand, not forced. On `update` it is what takes up a feature the shard does not already have. |
|
||
| `--patches-unsupported-servuo` | `install` | Required *in addition to* `--patches` to run the patch tier on a ServUO that is not 57.4. Unsupported and untested — see [§4](#4-the-patch-tier-optional). Ignored on 57.4. |
|
||
| `--host <name>` | `install` | The hostname to print in the website URLs. |
|
||
| `--site-url <url>` | `install` | Your site's base URL, for the Admin → Shard link. |
|
||
| `--yes` | all | Assume the default answer to every prompt. Combine with the flags above for an unattended run. **On `uninstall` it means yes** — that prompt defaults to no, and typing `uninstall --yes` is not an accident. |
|
||
| `--purge` | `uninstall` | Also delete `sidecar.toml`, `uo-link.db` and the cached patch set, all of which are otherwise kept. |
|
||
|
||
Exit codes are `0` success, `1` the run failed, `2` the arguments were unusable. Two commands also
|
||
use `1` for a run that *completed* and found something wrong, so they can be read from a script:
|
||
`doctor` when any check failed, and `uninstall` when a step could not be carried out (everything
|
||
else still was).
|
||
|
||
---
|
||
|
||
## 3. Where everything lands
|
||
|
||
**Linux**
|
||
|
||
| Path | What |
|
||
|---|---|
|
||
| `/usr/bin/runicgateway-link` | The sidecar binary |
|
||
| `/etc/runicgateway/sidecar.toml` | Sidecar config, including the auth token |
|
||
| `/etc/runicgateway/install.json` | What the installer deployed: versions, commit, per-file hashes, applied patches, timestamps |
|
||
| `/etc/runicgateway/patches/` | Copies of the patches the tier evaluated, so `uninstall` can print the exact hunks long after the release tarball is gone, and a refused one is still on hand to apply yourself |
|
||
| `/etc/runicgateway/patches/originals/` | Each file the patch tier edited, exactly as it was beforehand — a revert you can verify rather than reconstruct |
|
||
| `/var/lib/runicgateway/uo-link.db` | The sidecar's SQLite store (event history, cached profiles, link map) |
|
||
| `/etc/systemd/system/runicgateway-link.service` | The service unit, running as a dedicated user |
|
||
|
||
**Windows**
|
||
|
||
| Path | What |
|
||
|---|---|
|
||
| `%ProgramFiles%\RunicGateway\uo-link-sidecar.exe` | The sidecar binary |
|
||
| `%ProgramData%\RunicGateway\sidecar.toml` | Sidecar config, including the auth token |
|
||
| `%ProgramData%\RunicGateway\install.json` | As above |
|
||
| `%ProgramData%\RunicGateway\patches\` | As above |
|
||
| `%ProgramData%\RunicGateway\patches\originals\` | As above |
|
||
| `%ProgramData%\RunicGateway\uo-link.db` | The sidecar's SQLite store |
|
||
| Service `RunicGatewayLink` | Automatic start, restart on failure, running as `NT SERVICE\RunicGatewayLink` |
|
||
|
||
**Inside your ServUO tree** (added by the overlay sync — 24 files):
|
||
|
||
```
|
||
Config/Bridge.cfg every bridge setting, heavily commented
|
||
Scripts/Custom/Bridge/*.cs 22 files: the plugin itself
|
||
Scripts/Scripts.csproj OVERWRITES a stock file (see below)
|
||
```
|
||
|
||
**Both service definitions pin the config path**, because the sidecar's own default is relative to
|
||
its working directory — and a service manager's working directory is not somewhere you want a
|
||
database or a config file. On Windows it can be `%SystemRoot%\System32` or, under
|
||
`C:\Program Files\`, a silently redirected VirtualStore copy.
|
||
|
||
How the *database* path is pinned differs by platform, and that is deliberate:
|
||
|
||
| | Config | Database |
|
||
|---|---|---|
|
||
| **Linux** | `Environment=UOLINK_CONFIG=` in the unit | `Environment=UOLINK_DB_PATH=` in the unit — `/etc` and `/var/lib` are different directories, so both need naming |
|
||
| **Windows** | `--config` inside the service's own `binPath` | nothing to set: a relative `[store] path` resolves against the config's directory, which *is* `%ProgramData%\RunicGateway` |
|
||
|
||
The Windows service would otherwise need a **machine-wide** environment variable — `sc.exe` has no
|
||
per-service one — which every process on the host inherits and which outlives an uninstall.
|
||
|
||
**Both run as a dedicated, unprivileged account.** Linux gets a `runicgateway` system user; Windows
|
||
gets a virtual service account, `NT SERVICE\RunicGatewayLink`, which Windows creates as part of
|
||
registering the service and which has no password. Neither runs as root or `LocalSystem`.
|
||
|
||
**`sidecar.toml` is locked down, because it holds your auth token.** Neither default location
|
||
protects it on its own — `/etc` is world-readable, and `%ProgramData%` grants `Users` read access by
|
||
inheritance — so the installer sets the permissions itself: `chmod 600` plus `chown` to the service
|
||
user on Linux, and an explicit ACL of SYSTEM, Administrators and the service account on Windows.
|
||
|
||
> **`Scripts.csproj` is overwritten deliberately.** The stock file omits `Scripts/Custom/`, so the
|
||
> plugin would sit in the tree and never compile — and ServUO would not tell you, because it
|
||
> ignores the script build's exit code and silently reloads the previous `Scripts.dll`. That
|
||
> failure mode is the reason [§6](#6-start-servuo-and-verify) exists.
|
||
|
||
---
|
||
|
||
## 4. The patch tier (optional)
|
||
|
||
Most of the plugin ships as **added** files, which is why the base install is a safe file copy. Two
|
||
features cannot: they need edits to stock ServUO sources, because the events they depend on do not
|
||
exist.
|
||
|
||
| Patch | Edits | Gives you | Rebuild needed |
|
||
|---|---|---|---|
|
||
| `playervendor-sale-eventsink.patch` + `playervendor-sale-gump.patch` | `Server/EventSink.cs`, `Scripts/Gumps/PlayerVendorGumps.cs` | `vendor.sale` events — player-vendor purchases with buyer, owner, price and commission, which is what cheat detection needs | **Core solution rebuild** (`dotnet build ServUO.sln`) — the dynamic script build is not enough |
|
||
| `commandlogging-event.patch` | `Scripts/Commands/Logging.cs` | In-game moderation actions (`[ban`, `[kick`, `[bcast`) forwarded to the website's moderation log as `admin.audit` | Script build only — a shard restart is enough |
|
||
|
||
Each patch has a companion `.cs` file that is copied **only after** its patch applies, because it
|
||
references symbols the patch introduces. That is why they are not in the base overlay: shipping them
|
||
unconditionally would break the build on every unpatched install.
|
||
|
||
How the installer handles it:
|
||
|
||
- **Opt-in.** The base install completes without it, and declining is a supported outcome, not a
|
||
degraded one.
|
||
- **Dry-run first, always.** Every patch is checked before anything is applied, and reported per
|
||
patch. Most real shards are hand-modified; a patch that does not apply is expected, not alarming.
|
||
- **A modified file is not automatically a refusal.** These patches touch three small regions of
|
||
three large files. If you have edited `Logging.cs` somewhere else entirely, the installer says so
|
||
and still applies the patch — it checks whether *the lines the patch edits* are still stock, not
|
||
whether the whole file is. It applies only where the surrounding lines match the patch exactly and
|
||
appear exactly once; anything less and it stops and hands you the hunk to apply by hand. It never
|
||
force-fits a patch by loosening the match.
|
||
- **All or nothing per feature.** The two vendor-sale patches are one unit and are applied together
|
||
or not at all — and within a patch, if one hunk cannot be placed safely, none are. A patch that
|
||
could have been placed but was held back by its sibling says exactly that; it is never reported as
|
||
applied.
|
||
- **It does not need `git`, and does not use it.** The matching and the writing are the installer's
|
||
own, which is why it can place a patch on a shard where `git apply` refuses — the shipped patches
|
||
and their target files do not all use the same line endings, and that alone defeats `git apply`.
|
||
Nothing outside a patched region is touched, down to the byte, and inserted lines take your file's
|
||
own line ending.
|
||
- **Your ServUO version is reported, not decisive** — but see the warning below before running this
|
||
on anything other than 57.4.
|
||
- **Recorded, and the `.patch` files cached**, so re-runs stay idempotent and `uninstall` can print
|
||
the exact hunks to revert — along with how each was applied, since a patch placed into a file you
|
||
had already modified is one to look at more carefully when reverting. Patches that were *not*
|
||
applied are cached too, because that is the copy the run tells you to apply by hand.
|
||
- **A copy of every file it edits is kept, exactly as it was beforehand**, under
|
||
`patches/originals/` in the installer's own directory — not in your ServUO tree. It is written
|
||
before the first edit and never overwritten, so however many times you re-run `install`, it stays
|
||
the version from before the tier ever touched the file. That is what lets you verify a revert
|
||
rather than reconstruct one.
|
||
- **Re-running is safe.** A patch already in place is recognised and left alone, and the record
|
||
keeps the way it originally landed rather than relabelling it.
|
||
|
||
### ⚠ On any ServUO that is not 57.4: unsupported, untested, no guarantees
|
||
|
||
> **Runic Gateway is designed, built and tested against stock ServUO 57.4.** That is the only
|
||
> supported version.
|
||
>
|
||
> On any other version — a newer release, an older one, or a fork — the patch tier is
|
||
> **UNSUPPORTED, UNTESTED, and NOT GUARANTEED TO WORK.** You may run it. If you do, you are on your
|
||
> own: it is not covered by support, and a bad outcome may not show up until your shard is live,
|
||
> because ServUO's script build reports success even when it failed and quietly keeps running the
|
||
> previous `Scripts.dll`.
|
||
>
|
||
> The installer will still refuse to place a patch anywhere the exact lines it edits have changed —
|
||
> but matching text is not the same as matching behaviour. A hunk can land correctly and still be
|
||
> wrong for a tree that has diverged around it.
|
||
>
|
||
> **Back up your ServUO tree first, and verify your shard boots and compiles afterwards.**
|
||
|
||
Because of that, on a non-57.4 tree the tier is off by default and takes a deliberate yes:
|
||
|
||
- the interactive prompt defaults to **no** and prints the warning above;
|
||
- `--patches` on its own is **not** enough — an unattended run must also pass
|
||
`--patches-unsupported-servuo`;
|
||
- the choice is recorded, and `doctor` keeps showing an unsupported-version row for the life of the
|
||
install — so whoever looks after this shard next can see it without being told.
|
||
|
||
A run where the tier is selected on a shard that has been worked on looks like this:
|
||
|
||
```
|
||
Patch tier 2 of 3 applied
|
||
✓ playervendor-sale-eventsink Server/EventSink.cs
|
||
stock file — applied at line 171, 1521, 1771, 2416
|
||
✓ playervendor-sale-gump Scripts/Gumps/PlayerVendorGumps.cs
|
||
file modified, patched region stock — applied at line 95
|
||
✗ commandlogging-event Scripts/Commands/Logging.cs
|
||
patched region has been modified (hunk 1) — not applied
|
||
apply this by hand, then re-run install to record it:
|
||
/etc/runicgateway/patches/commandlogging-event.patch
|
||
|
||
⚠ Server/EventSink.cs — a CORE ServUO file was patched. Rebuild the solution:
|
||
dotnet build ServUO.sln
|
||
A shard restart is not enough; ServUO's dynamic script build does not rebuild the core, and it
|
||
will not tell you so.
|
||
|
||
Not applied, so you do not get: no in-game moderation audit forwarding.
|
||
Everything else works. Apply the hunks by hand if you want them, then re-run install to record it.
|
||
```
|
||
|
||
The line numbers are where each hunk was actually found in *your* file, not where it sits in stock
|
||
ServUO — they differ as soon as anything above the region has been edited, and yours is the one to
|
||
go to.
|
||
|
||
If it is skipped or fails, you lose exactly two things — **`vendor.sale` events** and **in-game
|
||
moderation audit forwarding**. Everything else works. You can apply the patches later by hand (see
|
||
`patches/README.md` in the tarball) and re-run `install` to record it.
|
||
|
||
---
|
||
|
||
## 5. Connect the website
|
||
|
||
The installer ends a successful run by printing the one manual step it cannot do for you:
|
||
|
||
```
|
||
Runic Gateway is installed.
|
||
|
||
One manual step remains — connect the website to this sidecar:
|
||
|
||
Base URL http://shard.example.com:8080
|
||
WebSocket URL ws://shard.example.com: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.example/admin/shard
|
||
|
||
The token is write-only once saved — the site will never show it back to you.
|
||
```
|
||
|
||
Every value there comes from asking the installed sidecar itself (`--print-config`), not from a
|
||
log file or a guess, so it cannot drift from what the service actually runs.
|
||
|
||
On your site, sign in as an administrator and open **Admin → Shard (uo-link)**:
|
||
|
||
| Field on the page | Paste |
|
||
|---|---|
|
||
| Enable the shard integration | ✔ on |
|
||
| Base URL (REST) | the **Base URL** line |
|
||
| WebSocket URL (feed) | the **WebSocket URL** line |
|
||
| Auth token | the **Auth token** line |
|
||
| Protocol | the **Protocol version** line (`3`) |
|
||
|
||
Saving restarts the site's ingest client, so the change takes effect immediately. The token is
|
||
AES-GCM encrypted at rest and **never returned to any client** — losing it means reading it back
|
||
from `sidecar.toml` on the shard host, not from the website.
|
||
|
||
### If your website is on a different machine
|
||
|
||
The sidecar binds `127.0.0.1:8080` by default, which is reachable only from the shard host. If your
|
||
website runs elsewhere, you must widen the bind — and then narrow the access:
|
||
|
||
1. Set `[web] bind` in `sidecar.toml` to `0.0.0.0:8080` (or a specific LAN address) and restart the
|
||
service.
|
||
2. **Firewall port 8080 to your website's address only.** The auth token is always on, but it
|
||
travels as a plain bearer token — the sidecar speaks HTTP, not HTTPS.
|
||
3. If the two hosts are not on a trusted network, put the sidecar behind a TLS reverse proxy or a
|
||
VPN/WireGuard link, and give the website the proxied `https://` / `wss://` URLs.
|
||
|
||
The `[shard] bind` line is a different matter: leave it on `127.0.0.1:7788`. That socket accepts
|
||
*inbound commands* to the game, and being loopback-only is what makes that safe.
|
||
|
||
---
|
||
|
||
## 6. Start ServUO and verify
|
||
|
||
Start your shard the way you always do. Then confirm the bridge is actually live — not merely
|
||
installed. **A successful file copy is not a working bridge**: ServUO shells out to `dotnet build`,
|
||
prints the output, ignores the exit code, and reloads the existing `Scripts.dll`, so a broken script
|
||
build looks exactly like a clean boot.
|
||
|
||
**a. Watch the boot output.** You want to see the build succeed *and* the bridge announce itself:
|
||
|
||
```
|
||
Core: Compiling scripts...
|
||
Build succeeded.
|
||
[Bridge] enabled=True endpoint=127.0.0.1:7788 queueCap=10000 sweeps(stat=30s decay=60s …
|
||
```
|
||
|
||
If you scrolled past it, force the question:
|
||
|
||
```bash
|
||
cd <servuo root>
|
||
dotnet build Scripts/Scripts.csproj -c Release -p:Platform=x64 # must be 0 errors
|
||
```
|
||
|
||
**b. Ask the shard, in game.** As an Administrator:
|
||
|
||
```
|
||
[bridge status
|
||
```
|
||
|
||
It reports the config plus `connected=True depth=0 sent=… dropped=0 …`. `connected=False` means the
|
||
shard cannot reach the sidecar; `dropped` climbing means the sidecar is wedged and the shard is
|
||
shedding events rather than stalling — which it is designed to do. `[bridge reload` re-reads
|
||
`Bridge.cfg` without a restart; `[bridge sweepnow` forces one pass of every stream.
|
||
|
||
**c. Ask the sidecar.** `/health` needs no auth, so it is safe to curl from a terminal:
|
||
|
||
```bash
|
||
curl -s http://127.0.0.1:8080/health
|
||
{"status":"ok","protocol":3,"plugin_connected":true,"database":"ok","uptime":"2m","last_event":"2026-08-04T18:22:10.412Z"}
|
||
```
|
||
|
||
`plugin_connected: true` is the one that matters — it is the only value in this whole guide that
|
||
distinguishes "files copied" from "the bridge works".
|
||
|
||
**d. Ask the website.** The public site should stop showing the shard as offline, and live events
|
||
should appear on the admin dashboard within seconds.
|
||
|
||
---
|
||
|
||
## 7. Day two
|
||
|
||
### `runicgateway doctor`
|
||
|
||
The command that makes this supportable. Run it before asking anyone for help — its output is the
|
||
first thing a maintainer will want.
|
||
|
||
```
|
||
✓ Install record /etc/runicgateway/install.json (bundle 2026.08.04, installer 1.0.0, …)
|
||
✓ ServUO found /opt/ServUO (57.4)
|
||
✓ Overlay in sync 24 files, all hashes match install.json
|
||
⚠ Patch tier 1 applied — moderation-audit (region-match)
|
||
✓ uo-link installed uo-link-sidecar 1.1.0 (protocol 3)
|
||
config /etc/runicgateway/sidecar.toml database /var/lib/runicgateway/uo-link.db
|
||
✓ Service runicgateway-link.service active, enabled as runicgateway
|
||
✓ Sidecar reachable 127.0.0.1:8080 /health ok, up 6h, database ok
|
||
✓ Protocol sidecar 3 = overlay manifest 3
|
||
✗ Shard connected no — the shard is running (pid 8123) but has not dialed in
|
||
✓ Bundle 2026.08.04 — up to date
|
||
```
|
||
|
||
Rows come from asking the installed sidecar (`--version`, `--print-config`) rather than from reading
|
||
`install.json`, so `doctor` reports what the binary would actually do — including which config and
|
||
database file the *service* resolves — rather than what the installer believes it was told. The
|
||
overlay row compares live file hashes against `install.json`, which is how it tells "you edited a
|
||
deployed file" from "the file is gone"; the bundle row is what tells you the overlay upstream has
|
||
moved on. Each patched file is re-checked against the cached copy of its patch, so a core upgrade or
|
||
a restored backup that quietly removed the tier's edits is caught here — nothing else would notice.
|
||
|
||
It writes nothing at all, and it is safe to run while the shard is up; that is in fact the only
|
||
state in which the last row can be `✓`.
|
||
|
||
**Reading the marks:**
|
||
|
||
| | |
|
||
|---|---|
|
||
| `✓` | as it should be |
|
||
| `⚠` | worth knowing, not broken — a stopped shard, a service you never registered, an unpatched tier, or no route to Gitea to check for a newer bundle |
|
||
| `✗` | broken. `doctor` exits `1` if any row is `✗`, so it can be run from a monitoring script; a `⚠` never causes that |
|
||
|
||
The distinction on the last row is worth spelling out: **shard not running** is a `⚠` (start it),
|
||
while **shard running and not dialed in** is a `✗` — that is the silent failure this whole guide
|
||
warns about, where ServUO reports a clean boot over a script build that failed.
|
||
|
||
### `runicgateway update`
|
||
|
||
Re-resolves the bundle and moves both halves to a combination whose protocol versions were checked
|
||
together — never to two independently-latest artifacts that may disagree.
|
||
|
||
- **Sidecar**: download → verify → replace binary → restart service. No shard downtime.
|
||
- **Overlay**: download → verify → re-sync → record the new commit → **tell you to restart ServUO.**
|
||
It does not restart your shard.
|
||
|
||
Your `sidecar.toml`, your `Bridge.cfg` edits and your database are not touched. `Bridge.cfg` is
|
||
overwritten only if you have not changed it; a modified copy is reported, not clobbered.
|
||
|
||
It updates the ServUO tree `install.json` names — not a tree it detects — and it needs the shard
|
||
stopped, exactly as `install` does. There is nothing to update on a host that was never installed;
|
||
it says so rather than performing a first install under a verb that promises to preserve.
|
||
|
||
**Your auth token is not reprinted.** It has not changed and your website already has it. The one
|
||
thing an update can change that the site must be told about is the **protocol version**, and it says
|
||
so plainly when that happens — a stale number in Admin → Shard is answered with `409` and looks
|
||
exactly like your shard going offline.
|
||
|
||
**The patch tier under `update`:** features you already have are re-checked against the new release
|
||
(normally nothing to do), without asking you again — you consented when they were installed, and
|
||
that includes a shard where the tier ran unsupported. Features you never took are **named, not
|
||
applied**; run `update --patches` (or `install --patches`) to take one up. A shard that declined the
|
||
tier stays unpatched through every update.
|
||
|
||
### `runicgateway uninstall`
|
||
|
||
Removes what it exclusively owns, and **prints** everything else. The installer cannot know what you
|
||
have changed in your own server tree since deployment, so an automatic revert risks silently eating
|
||
your work.
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Removed** | The sidecar binary, its service entry, `install.json` |
|
||
| **Kept** | `sidecar.toml`, `uo-link.db`, and the cached patch set with its pre-patch originals (`--purge` drops all of them) |
|
||
| **Printed, not done** | Every overlay file deployed into your ServUO tree, by path, for you to delete — with any file you have edited since deployment flagged, so you do not delete your own work by mistake |
|
||
| **Printed, not done** | The exact hunks each applied patch added to `EventSink.cs`, `PlayerVendorGumps.cs` and `Logging.cs`, for you to revert — with how each landed, since one placed into a file you had already modified is worth a closer look. The pre-patch copy kept under `patches/originals/` is there to diff against. |
|
||
|
||
It lists all of that **before** asking, and the prompt defaults to **no**. `--yes` proceeds, which is
|
||
what an unattended uninstall needs; nothing else about the command is destructive to your shard,
|
||
which is neither stopped nor started.
|
||
|
||
The report is also written to a file — `runicgateway-uninstall-<timestamp>.txt` in the directory you
|
||
ran the command from — so it survives the scrollback. That is why the cached patches and the
|
||
originals stay behind by default: they are the only offline record of what the tier changed once the
|
||
release tarball is gone, and the report tells you to diff against them.
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
| Symptom | Cause and fix |
|
||
|---|---|
|
||
| **Windows asks for Administrator as soon as you launch it** | Expected, and it needs Administrator anyway. Windows applies *installer detection* to unsigned executables whose file name contains `install` and elevates them before the program starts. Run it from an already-elevated PowerShell and you will not see the prompt. |
|
||
| **"ServUO is running — stop it before installing"** | Correct, and not overridable. `ServUO.exe` locks `Scripts.dll` and rewrites `Saves/` on exit; deploying underneath it corrupts one or both. Stop the shard, install, start it again. |
|
||
| Shard boots clean but nothing reaches the site | The classic silent failure: ServUO ignores the script build's exit code and reloaded a **stale `Scripts.dll`**. Run `dotnet build Scripts/Scripts.csproj -c Release -p:Platform=x64` and read the errors it prints. |
|
||
| `[bridge status` says `connected=False` | The sidecar is not listening on `127.0.0.1:7788`. Check the service is running, and that `[shard] bind` in `sidecar.toml` matches `Host`/`Port` in `Bridge.cfg`. |
|
||
| `[bridge` is not a command | The plugin did not compile, or `Bridge.cfg` has the bridge disabled. See the row above. |
|
||
| Website says the shard is offline; `/health` is fine locally | The website cannot reach port 8080 — bind address or firewall. See [§5](#if-your-website-is-on-a-different-machine). Note that the site is *designed* to render normally with the shard offline, so this fails quietly by design. |
|
||
| 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/`. |
|
||
| 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. |
|
||
| Token leaked into a log or a screenshot | Clear `[web] auth_token` in `sidecar.toml`, restart the service (a new token is generated and saved), read it back with `--print-config`, and re-save it in Admin → Shard. |
|
||
|
||
---
|
||
|
||
## Appendix A — installing by hand
|
||
|
||
This is what the installer automates. It works today, on the current releases, and is the fallback
|
||
whenever you would rather not run an unsigned binary.
|
||
|
||
Throughout: `<servuo>` is your ServUO root, and **the shard is stopped**.
|
||
|
||
### A1. Fetch the bundle (so you install a checked pair)
|
||
|
||
```bash
|
||
curl -s https://gitea.whitlocktech.com/RunicGateway/installer/raw/branch/main/bundles/current.json
|
||
```
|
||
|
||
It names the sidecar tag, the overlay tag, their agreed `protocol`, and the SHA256 of every asset.
|
||
Use those versions together; that pairing is the only thing CI has verified.
|
||
|
||
### A2. Deploy the plugin overlay
|
||
|
||
```bash
|
||
curl -LO https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/releases/download/v0.1.1/runicgateway-overlay-0.1.1.tar.gz
|
||
curl -LO https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/releases/download/v0.1.1/SHA256SUMS
|
||
sha256sum -c SHA256SUMS --ignore-missing # must say: OK
|
||
|
||
tar xzf runicgateway-overlay-0.1.1.tar.gz # → runicgateway-overlay/
|
||
cd runicgateway-overlay
|
||
cat manifest.json # version, commit, protocol, per-file hashes
|
||
|
||
cp -r overlay/. <servuo>/ # adds files; overwrites Scripts/Scripts.csproj
|
||
```
|
||
|
||
On Windows, `Expand-Archive` does not read `.tar.gz`; use `tar.exe` (shipped with Windows 10+) and
|
||
`Copy-Item -Recurse -Force`. Plugin developers have `deploy.ps1` in the source repo, which does the
|
||
same copy with a hash diff and a `-Verify` dry run — it is not shipped in the tarball.
|
||
|
||
The overlay only ever **adds or overwrites**. Nothing in your tree is deleted.
|
||
|
||
*Optional — the patch tier* (stock ServUO 57.4 only; see `patches/README.md` in the tarball for the
|
||
full explanation):
|
||
|
||
```bash
|
||
cd <servuo>
|
||
git apply --check patches/playervendor-sale-eventsink.patch patches/playervendor-sale-gump.patch
|
||
git apply patches/playervendor-sale-eventsink.patch patches/playervendor-sale-gump.patch
|
||
cp patches/BridgeVendorSale.cs Scripts/Custom/Bridge/
|
||
dotnet build ServUO.sln # REQUIRED — EventSink.cs is a core file
|
||
|
||
git apply --check patches/commandlogging-event.patch
|
||
git apply patches/commandlogging-event.patch
|
||
cp patches/BridgeModerationAudit.cs Scripts/Custom/Bridge/
|
||
```
|
||
|
||
`git apply` works in a plain directory — the shard does not need to be a git repo. If you use
|
||
`patch` instead, note that some core files are CRLF while others are LF: use `patch --binary`.
|
||
|
||
**If `git apply` refuses a patch whose target region is visibly untouched, line endings are the
|
||
usual cause** — the `.patch` files and their targets do not all use the same ones, and `git apply`
|
||
compares them literally. The installer's own tier normalizes line endings and trailing whitespace
|
||
for the *comparison* while writing back your file's own endings, which is why it can place patches
|
||
`git apply` rejects. Running the installer is the easier route here.
|
||
|
||
### A3. Install the sidecar
|
||
|
||
```bash
|
||
curl -LO https://gitea.whitlocktech.com/RunicGateway/link/releases/download/v1.1.0/uo-link-sidecar-linux-x86_64
|
||
curl -LO https://gitea.whitlocktech.com/RunicGateway/link/releases/download/v1.1.0/SHA256SUMS
|
||
sha256sum -c SHA256SUMS --ignore-missing
|
||
|
||
sudo install -m 0755 uo-link-sidecar-linux-x86_64 /usr/bin/runicgateway-link
|
||
sudo mkdir -p /etc/runicgateway /var/lib/runicgateway
|
||
```
|
||
|
||
Provision the config and read back the token in one step. `--print-config` writes the file if it is
|
||
missing, generates the auth token if there is none, and prints the resolved settings as JSON — it is
|
||
the supported alternative to scraping the startup log:
|
||
|
||
```bash
|
||
sudo UOLINK_DB_PATH=/var/lib/runicgateway/uo-link.db \
|
||
/usr/bin/runicgateway-link --print-config --config /etc/runicgateway/sidecar.toml
|
||
```
|
||
|
||
```json
|
||
{
|
||
"component": "uo-link-sidecar",
|
||
"version": "1.1.0",
|
||
"protocol": 3,
|
||
"config_path": "/etc/runicgateway/sidecar.toml",
|
||
"config_created": true,
|
||
"token_generated": true,
|
||
"shard": { "bind": "127.0.0.1:7788" },
|
||
"web": {
|
||
"bind": "127.0.0.1:8080",
|
||
"ws_path": "/ws",
|
||
"auth_required": true,
|
||
"auth_token": "4f9c…"
|
||
},
|
||
"store": { "path": "/var/lib/runicgateway/uo-link.db" }
|
||
}
|
||
```
|
||
|
||
`config_created` and `token_generated` tell you whether *this* run provisioned anything — the values
|
||
alone cannot distinguish a fresh install from a re-read. **The output contains the auth token in
|
||
clear text**: keep it out of shell transcripts, logs and support bundles.
|
||
|
||
### A4. Register the service
|
||
|
||
**Linux** — `/etc/systemd/system/runicgateway-link.service`:
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=Runic Gateway uo-link sidecar
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=runicgateway
|
||
Environment=UOLINK_CONFIG=/etc/runicgateway/sidecar.toml
|
||
Environment=UOLINK_DB_PATH=/var/lib/runicgateway/uo-link.db
|
||
ExecStart=/usr/bin/runicgateway-link
|
||
Restart=on-failure
|
||
RestartSec=5
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
```bash
|
||
sudo useradd --system --no-create-home runicgateway
|
||
sudo chown -R runicgateway /var/lib/runicgateway /etc/runicgateway
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now runicgateway-link
|
||
systemctl status runicgateway-link
|
||
```
|
||
|
||
**Windows** (elevated PowerShell) — binary under `%ProgramFiles%`, data under `%ProgramData%`:
|
||
|
||
```powershell
|
||
New-Item -ItemType Directory -Force "$env:ProgramFiles\RunicGateway", "$env:ProgramData\RunicGateway" | Out-Null
|
||
Copy-Item .\uo-link-sidecar-windows-x86_64.exe "$env:ProgramFiles\RunicGateway\uo-link-sidecar.exe"
|
||
|
||
& "$env:ProgramFiles\RunicGateway\uo-link-sidecar.exe" --print-config --config "$env:ProgramData\RunicGateway\sidecar.toml"
|
||
|
||
# The config file now holds your auth token. Lock it down before anything else can read it:
|
||
icacls "$env:ProgramData\RunicGateway\sidecar.toml" /inheritance:r /grant:r '*S-1-5-18:(F)' /grant:r '*S-1-5-32-544:(F)'
|
||
|
||
# binPath carries the config path. The single quotes matter: the value itself contains the double
|
||
# quotes the service manager needs around a path with spaces in it.
|
||
sc.exe create RunicGatewayLink `
|
||
binPath= '"C:\Program Files\RunicGateway\uo-link-sidecar.exe" --config "C:\ProgramData\RunicGateway\sidecar.toml"' `
|
||
obj= 'NT SERVICE\RunicGatewayLink' start= auto
|
||
sc.exe failure RunicGatewayLink reset= 86400 actions= restart/5000
|
||
|
||
# The service account exists only once sc create has created it, so its grants come after:
|
||
icacls "$env:ProgramData\RunicGateway\sidecar.toml" /grant 'NT SERVICE\RunicGatewayLink:(R)'
|
||
icacls "$env:ProgramData\RunicGateway" /grant 'NT SERVICE\RunicGatewayLink:(OI)(CI)M'
|
||
|
||
sc.exe start RunicGatewayLink
|
||
```
|
||
|
||
Three things there are easy to get wrong:
|
||
|
||
- **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
|
||
the service's working directory, which for a service is `%SystemRoot%\System32`.
|
||
- **The database needs no pinning here.** A relative `[store] path` resolves against the directory
|
||
holding `sidecar.toml`, which is already `%ProgramData%\RunicGateway`.
|
||
- **`obj=` is what keeps this off `LocalSystem`.** `NT SERVICE\RunicGatewayLink` is a virtual
|
||
service account: Windows creates it with the service, it has no password, and it exists only for
|
||
this service. Omit `obj=` and you get the most privileged local identity there is, for a process
|
||
listening on two TCP ports.
|
||
|
||
### 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
|
||
values into Admin → Shard, start ServUO, then check `[bridge status` in game and `/health` on the
|
||
sidecar.
|
||
|
||
### A6. Updating by hand
|
||
|
||
Re-read `current.json`, and if either version moved: replace the sidecar binary and restart its
|
||
service; re-extract the overlay tarball over your tree and restart ServUO. Keep the two in step —
|
||
`current.json` is the only statement that a given pair speaks the same protocol.
|
||
|
||
---
|
||
|
||
## Appendix B — `sidecar.toml` reference
|
||
|
||
Written on first run with a generated token. Environment variables override the file; the file
|
||
overrides these defaults.
|
||
|
||
```toml
|
||
[shard]
|
||
bind = "127.0.0.1:7788" # where the SHARD dials in. Keep this on loopback.
|
||
|
||
[web]
|
||
bind = "127.0.0.1:8080" # where the WEBSITE connects. Widen only with a firewall in front.
|
||
auth_token = "…" # generated if blank; the website's Admin → Shard "Auth token"
|
||
|
||
[store]
|
||
path = "uo-link.db" # relative paths resolve against this file's directory, not the CWD
|
||
```
|
||
|
||
| Environment variable | Overrides |
|
||
|---|---|
|
||
| `UOLINK_CONFIG` | Which config file to read (`--config <PATH>` outranks it) |
|
||
| `UOLINK_SHARD_BIND` | `[shard] bind` |
|
||
| `UOLINK_WEB_BIND` | `[web] bind` |
|
||
| `UOLINK_WEB_TOKEN` | `[web] auth_token` |
|
||
| `UOLINK_DB_PATH` | `[store] path` |
|
||
|
||
| Sidecar command | Output |
|
||
|---|---|
|
||
| `uo-link-sidecar --version` | `uo-link-sidecar 1.1.0 (protocol 3)` |
|
||
| `uo-link-sidecar --print-config [--config PATH]` | The JSON in [A3](#a3-install-the-sidecar). Provisions on first run. **Contains the token.** |
|
||
| `uo-link-sidecar --help` | Usage. An unrecognized argument exits `2` rather than starting a sidecar you did not ask for. |
|
||
|
||
Authentication is **always on**: a blank token is generated and written back, so the web surface is
|
||
never unauthenticated. `/health` is the one unauthenticated route, so monitoring can reach it.
|
||
|
||
---
|
||
|
||
## Appendix C — `Config/Bridge.cfg` settings worth reviewing
|
||
|
||
The file is deployed heavily commented and every setting has a working default — you can leave it
|
||
entirely alone. These are the ones most shards want to look at once. Run `[bridge reload` after
|
||
editing; endpoint changes take effect on the next reconnect.
|
||
|
||
| Setting | Default | Why you might change it |
|
||
|---|---|---|
|
||
| `LinkUrl` | `https://yoursite/link` | Shown in game when a player runs `[link` to connect their account. **Set this to your site.** |
|
||
| `PublicConnectAddress` | *(blank)* | The one connection detail the bridge will publish, e.g. `play.myshard.com,2593`. Blank omits it; `Server.cfg`'s address is **never** published automatically. |
|
||
| `AdminWriteEnabled` | `false` | Opt-in staff write plane: kick/ban/broadcast from the website. Authorization is enforced on the website; `AdminAccessFloor` is the shard-side floor that even a compromised sidecar cannot cross. |
|
||
| `MarketEnabled`, `MarketSweepSeconds`, `MarketSweepBatch` | `true`, `60`, `25` | The player-vendor index. Coverage takes `ceil(vendors / batch) × seconds` — 500 vendors is one full pass every 20 minutes at the defaults. |
|
||
| `PointsLeaderboardEnabled`, `PointsTopN`, `PointsSystems` | `true`, `10`, *(all shown on the loyalty gump)* | Standings boards. One frame **per system**, and ServUO carries ~25 of them, so a large `TopN` multiplies. |
|
||
| `RulesetEnabled`, `RulesetIncludeSchedule` | `true`, `true` | Publishes your ruleset (expansion, caps, systems on/off) to the site's rules page. Turn the schedule off if you would rather not advertise a predictable restart window. |
|
||
| `SignupMode` | `hybrid` | Which side may mint accounts — `website`, `game`, or `hybrid`. Pair `website` with `Accounts.AutoCreateAccounts=false`, or an in-game login still creates accounts. |
|
||
| `QueueCap` | `10000` | Outbound queue cap. On overflow the plugin **drops oldest** and counts drops, because a stalled sidecar must never take the shard down with it. |
|
||
|
||
Sweep intervals (`StatSweepSeconds`, `DecaySweepSeconds`, `EconomySweepSeconds`, and the rest) trade
|
||
freshness against Core-thread time. The measured cost is small — a vitals sweep is 0.0015 ms per
|
||
character, so 1000 online players is ~1.5 ms per pass — but there is rarely anything to gain by
|
||
hurrying them.
|
||
|
||
---
|
||
|
||
## Where to go next
|
||
|
||
| Doc | What |
|
||
|---|---|
|
||
| [PLAN.md](PLAN.md) | The installer's design of record — phases, locked decisions, the bundle model |
|
||
| [`bundles/README.md`](https://gitea.whitlocktech.com/RunicGateway/installer/src/branch/main/bundles/README.md) | The compat matrix: what a bundle is and how it is composed |
|
||
| [link/INTEGRATION.md](../link/INTEGRATION.md) | The sidecar's HTTP/WS API — for anyone integrating something other than the website |
|
||
| [link/ADMIN_CONTROLS.md](../link/ADMIN_CONTROLS.md) | The staff write plane in detail, before you turn `AdminWriteEnabled` on |
|
||
| [website/SHARD_VISIBILITY.md](../website/SHARD_VISIBILITY.md) | Which shard data each audience sees, configured on the website |
|
||
| [link/SHARD_PREREQS.md](../link/SHARD_PREREQS.md) | A worked example of diagnosing a shard whose scripts silently stopped compiling |
|