# uo-link — Rust sidecar The **Rust sidecar** half of the Runic Gateway bridge. The ServUO shard dials out to this sidecar over a loopback TCP socket (newline-delimited JSON); the sidecar owns the WebSocket + REST API the website consumes, along with auth, buffering, and fan-out. ``` ServUO plugin (C#, net48) ──loopback TCP, newline-JSON──► Rust sidecar ──WebSocket/JSON──► website (RunicGateway/servuo-plugins) >>> THIS REPO <<< ``` The shard never speaks WebSocket and exposes no port of its own — the sidecar is the only network-facing component, which is what keeps the game unreachable from the internet. ## Running a shard? Don't build this The [**Runic Gateway installer**](https://gitea.whitlocktech.com/RunicGateway/installer) installs this sidecar for you — the released binary, its config, a hardened service account and the service registration — alongside the shard plugin, in one run, on Linux or Windows: ```bash sudo ./runicgateway-installer-linux-x86_64 install ``` It ends by printing the base URL, WebSocket URL, protocol version and auth token to paste into **Admin → Shard** on your site. Guide: [installer/INSTALL.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/installer/INSTALL.md). Installing it yourself is supported too — the release binaries on this repo's [releases page](https://gitea.whitlocktech.com/RunicGateway/link/releases) are the same ones the installer fetches, and [INSTALL.md Appendix A3–A4](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/installer/INSTALL.md#a3-install-the-sidecar) covers placing the binary and registering the service by hand. Everything below this line is for **developing on the sidecar**. ## Related repos | Repo | What | |------|------| | **this** — `RunicGateway/link` | The Rust sidecar (`sidecar/`). | | [RunicGateway/installer](https://gitea.whitlocktech.com/RunicGateway/installer) | The **installer** — deploys this sidecar and the plugin onto a shard host. The supported way to set one up. | | [RunicGateway/servuo-plugins](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins) | The **C# ServUO plugin** — the shard side of the bridge (`overlay/`, `patches/`, `deploy.ps1`, test scaffolding). | | [RunicGateway/docs](https://gitea.whitlocktech.com/RunicGateway/docs) | All project documentation — design docs, protocol spec, integration guide, research. | ## Layout | Path | What | |------|------| | `sidecar/` | The Rust sidecar crate — terminates the loopback link to the shard, exposes WS + REST to the website. See [`sidecar/README.md`](sidecar/README.md). | | `.gitea/workflows/pr-checks.yml` | Gates every PR into `main` on `cargo fmt --check`, `cargo clippy -D warnings`, and `cargo test`. | | `.gitea/workflows/release.yml` | Builds + releases the sidecar binary (Linux + Windows) on every merge to `main`. | ## Build & run (development) Building from source is for working *on* the sidecar; a deployment gets its binary from a release, via the installer or by hand. The sidecar is a standard cargo crate: ```bash cd sidecar cargo build --release # binary at target/release/uo-link-sidecar cp sidecar.toml.example sidecar.toml # then edit cargo run --release ``` Deploying it by hand rather than developing on it: `--config ` names the config file (as does `$UOLINK_CONFIG`), and `--print-config` prints the resolved settings — **including the auth token the website needs** — as JSON, provisioning the config file on first run. That is the supported way to read the token back; it is not meant to be scraped from the log. ```bash uo-link-sidecar --print-config --config /etc/runicgateway/sidecar.toml ``` ### Running as a service The same binary runs in the foreground and as a system service — there is no `--service` flag to remember, because the process can tell how it was started. - **Linux/systemd** supervises any foreground process, so the unit just runs the binary. `SIGTERM` (what `systemctl stop` sends) and `SIGINT` both unwind it cleanly; logs go to the journal. - **Windows** cannot. The service control manager only supervises a process that connects back to it within ~30 seconds via `StartServiceCtrlDispatcher`; a plain console program registered with `sc.exe create` is killed with **error 1053** despite running perfectly. So on Windows the sidecar speaks that handshake: started by the SCM it runs as a service, started from a shell the connect fails with `ERROR_FAILED_SERVICE_CONTROLLER_CONNECT` and it falls through to an ordinary foreground run. It reports `Running` only once the shard port is bound and the store is open, and — having no console — logs to `uo-link-sidecar..log` beside its config, rolled daily. Only the starting and stopping is platform-specific: `src/app.rs` is the entire sidecar and is shared, while `src/windows.rs` and `src/unix.rs` do nothing but start it and tell it when to stop. The Windows crates are declared under `[target.'cfg(windows)'.dependencies]`, so Cargo neither resolves nor builds them for a Linux target. Registering the service is the installer's job; to do it by hand see [INSTALL.md Appendix A4](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/installer/INSTALL.md). `.gitea/workflows/release.yml` cross-compiles Linux + Windows binaries and cuts a Gitea release on every merge to `main` (conventional-commit versioning). See [`sidecar/README.md`](sidecar/README.md) for configuration and the wire protocol. Before that, `.gitea/workflows/pr-checks.yml` runs the same gates on every pull request into `main` — `cargo fmt --check`, `cargo clippy --all-targets -- -D warnings`, then `cargo test --locked`. Run them locally before pushing and the PR will be green: ```bash cd sidecar cargo fmt # or --check to just report cargo clippy --locked --all-targets -- -D warnings cargo test --locked ``` ## Deployment & compatibility The plugin ([RunicGateway/servuo-plugins](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins)) and this sidecar are deployed **together** but built **independently**: - The **plugin** is deployed as source into the ServUO server root and compiled by ServUO at boot — no build artifact, no CI build. - The **sidecar** is a standalone Rust binary released from this repo. The only coupling is the **loopback JSON protocol** (the shard dials `127.0.0.1`). Compatibility is a protocol concern, not a build-order one — keep the event/command catalog in sync across the two repos. Canonical spec: [PLAN.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/PLAN.md) §5/§7 and [INTEGRATION.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/INTEGRATION.md). Because a wedged or absent sidecar cannot stall the shard, either side can be deployed or restarted independently. --- ## 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).