Files
.profile/profile

Runic Gateway

A website + game bridge for private Ultima Online (ServUO) shards.

Runic Gateway is a self-hostable platform that gives a UO shard a public site, wiki, and admin panel — and wires it to the live in-game world so the site can show shard status, economy, IDOCs, player activity, and per-character sheets, and staff can push control commands back into the game. Branding is instance-configurable; UOMysticmoon is the first instance.

website · build images website · PR checks link · release


The pieces

Runic Gateway is four repositories that deploy together but build independently:

Repo Language What it is
website JavaScript (Node + React) The full-stack app — Express REST API + MariaDB + a React/Vite SPA (public site, wiki, admin panel). This is the thing players and staff actually visit.
link Rust The uo-link sidecar. Runs next to the shard, terminates a loopback link from the game, and exposes the authenticated WebSocket + REST API the website consumes. The only network-facing half of the bridge.
servuo-plugins C# The ServUO plugin — the shard side of the bridge. Compiled by ServUO at boot; dials the sidecar over loopback and emits game events / accepts commands.
docs Markdown All project documentation — design docs, the wire-protocol spec, the integration guide, research. Start here when you want the why.

How they fit together

ServUO shard  ──loopback TCP,──▶  uo-link sidecar  ──WebSocket + REST──▶  website backend  ──▶  browser
(servuo-plugins) newline-JSON      (link, Rust)        bearer-auth           (website, Node)      same-origin
                                                                                                  JSON / SSE
  • The shard is never exposed to the internet. It only dials 127.0.0.1. The sidecar is the sole network-facing component, and only the website's backend talks to it.
  • The website backend ingests a live event stream from the sidecar (logins, vitals, economy, vendor sales, deaths, IDOC decay, staff audit…) and makes point-in-time REST calls for rosters and character sheets. It then fans that out to browsers over Server-Sent Events — a public channel (safe kinds only) and an admin channel (everything).
  • Players link a game account to a website account with a one-time in-game code, which is what authorizes character reads. Staff can push town-crier messages and control commands back into the game.

The connection between website and sidecar (URL, shared-secret token, protocol version) is admin-managed in the database, not env — set once in the site's Admin → Shard panel. If the sidecar is absent or the shard is down, every shard surface degrades gracefully.


Quick start

You need two things running: the website (site + admin) and the link sidecar (the game bridge). The servuo-plugins get deployed into your ServUO server root and compile at shard boot — see that repo's README.

1. Website — the site + admin panel

Prereqs: Node.js 20+ and Docker (for MariaDB).

git clone https://gitea.whitlocktech.com/RunicGateway/website.git
cd website

# Start a MariaDB the backend can reach
docker run -d --name rg-db -p 3306:3306 \
  -e MARIADB_DATABASE=runic_gateway -e MARIADB_USER=runic \
  -e MARIADB_PASSWORD=devpass -e MARIADB_ROOT_PASSWORD=rootpass mariadb:11

# Configure + start the backend (terminal 1)
cp server/.env.example server/.env
#   set DB_HOST=127.0.0.1, DB_PORT=3306, DB_USER=runic, DB_PASSWORD=devpass,
#       JWT_SECRET=<anything>, ADMIN_USERNAME=admin, ADMIN_PASSWORD=<your password>
npm run install-server
npm run server            # nodemon → http://localhost:3000

# Start the frontend (terminal 2)
npm run install-client
npm run client            # Vite → http://localhost:5173

Open http://localhost:5173, sign in at /admin/login with the admin credentials you set, then flip Maintenance → Live on the Dashboard. Tables, defaults, and the first admin are created automatically on first boot.

Production (Docker Compose): the website ships a production-shaped docker-compose.yml that pulls prebuilt app + bot images and serves the built SPA from Express — cp .env.example .env, fill it in, then docker compose pull && docker compose up -d. See the website README for the full options.

Prereqs: Rust (cargo). Standard cargo crate:

git clone https://gitea.whitlocktech.com/RunicGateway/link.git
cd link/sidecar
cargo build --release          # binary at target/release/uo-link-sidecar
cp sidecar.toml.example sidecar.toml   # then edit (bind addrs, shared-secret token)
cargo run --release

Then point the website at it from Admin → Shard: set the sidecar's base URL, WebSocket URL, shared-secret token, and protocol version. Prebuilt Linux + Windows binaries are also cut as a Gitea release on every merge to main.

3. servuo-plugins — the shard side

Deployed as source into your ServUO server root and compiled by ServUO at boot (no build artifact, no CI). It dials the sidecar over loopback. See servuo-plugins for deploy steps and the compatibility notes.


Where to go next

  • Running / customizing the sitewebsite README (tech stack, API endpoints, env vars, security, branding, Swagger at /api/docs).
  • The bridge internalsdocs — design docs, the canonical wire-protocol spec, and the integration guide.
  • Deploying the sidecar / plugin togetherlink README and servuo-plugins.
Runic Gateway · whitlocktech@gmail.com