Files
.profile/README.md
claude 57533754c7 fix: restore readable README (was double-base64-encoded)
Re-commit the org profile as plain markdown. The prior commit passed
pre-encoded content, which the API base64-encoded a second time and
rendered as gibberish.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 01:11:39 +00:00

155 lines
8.8 KiB
Markdown

<div align="center">
# 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. Players get the same public content and self-service
on the web **or** a native Android app. Branding is instance-configurable.
[![website · build images](https://gitea.whitlocktech.com/RunicGateway/website/actions/workflows/build-images.yml/badge.svg)](https://gitea.whitlocktech.com/RunicGateway/website/actions?workflow=build-images.yml)
[![link · release](https://gitea.whitlocktech.com/RunicGateway/link/actions/workflows/release.yml/badge.svg)](https://gitea.whitlocktech.com/RunicGateway/link/actions?workflow=release.yml)
[![android · checks](https://gitea.whitlocktech.com/RunicGateway/Android-app/actions/workflows/pr-checks.yml/badge.svg)](https://gitea.whitlocktech.com/RunicGateway/Android-app/actions?workflow=pr-checks.yml)
</div>
---
## The pieces
Runic Gateway is five repositories that deploy together but build independently:
| Repo | Language | What it is |
|------|----------|------------|
| [**website**](https://gitea.whitlocktech.com/RunicGateway/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, and the single backend every client talks to. |
| [**link**](https://gitea.whitlocktech.com/RunicGateway/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**](https://gitea.whitlocktech.com/RunicGateway/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. |
| [**Android-app**](https://gitea.whitlocktech.com/RunicGateway/Android-app) | Kotlin (Jetpack Compose) | The **native Android client** — public content + player self-service, purely an API client of the website backend. Same features as the browser client *minus* every admin console. Never touches the sidecar or shard. |
| [**docs**](https://gitea.whitlocktech.com/RunicGateway/docs) | Markdown | All project documentation — design docs, the wire-protocol spec, the integration guide, the Android plan, research. Start here when you want the *why*. |
## How they fit together
```
┌─▶ browser same-origin JSON / SSE
ServUO shard ──loopback TCP,──▶ uo-link sidecar ──WS + REST──▶ website backend ─┤
(servuo-plugins) newline-JSON (link, Rust) bearer-auth (website, Node) └─▶ Android app REST + push (ntfy)
```
- 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).
- The **Android app** is *just another client of the website backend* — it speaks the same
public REST API and never talks to the sidecar or shard. It self-configures its server URL
on first run, so one build works against any shard, and receives push notifications through
the shard's self-hosted **ntfy** relay (no Google Play Services required).
- 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
The core of a live shard is **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; the **Android-app** is an optional client you point at your
running website — see each repo's README.
### 1. Website — the site + admin panel
Prereqs: **Node.js 20+** and **Docker** (for MariaDB).
```bash
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.
### 2. link — the uo-link sidecar
Prereqs: **Rust** (cargo). Standard cargo crate:
```bash
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](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins) for deploy
steps and the compatibility notes.
### 4. Android-app — the native client (optional)
Prereqs: **JDK 17** and the Android SDK. A single build works against any shard — the app
prompts for your website's URL on first run.
```bash
git clone https://gitea.whitlocktech.com/RunicGateway/Android-app.git
cd Android-app
./gradlew assembleDebug # debug APK → app/build/outputs/apk/debug/
./gradlew installDebug # install on a connected device / emulator
```
Kotlin + Jetpack Compose (Material 3), min SDK Android 10 (API 29). It surfaces the same
public content and player self-service as the browser — home/status, news, wiki, the shard
hub, account linking, character sheets — and opts into push notifications through the shard's
self-hosted **ntfy** relay. See the [Android-app README](https://gitea.whitlocktech.com/RunicGateway/Android-app)
and the design contract in [`docs/android/PLAN.md`](https://gitea.whitlocktech.com/RunicGateway/docs).
---
## Where to go next
- **Running / customizing the site** → [website README](https://gitea.whitlocktech.com/RunicGateway/website) (tech stack, API endpoints, env vars, security, branding, Swagger at `/api/docs`).
- **The Android client** → [Android-app README](https://gitea.whitlocktech.com/RunicGateway/Android-app) and [`docs/android/PLAN.md`](https://gitea.whitlocktech.com/RunicGateway/docs) — the authoritative design contract, milestones, and the push-notification architecture.
- **The bridge internals** → [docs](https://gitea.whitlocktech.com/RunicGateway/docs) — design docs, the canonical wire-protocol spec, and the integration guide.
- **Deploying the sidecar / plugin together** → [link README](https://gitea.whitlocktech.com/RunicGateway/link) and [servuo-plugins](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins).
<div align="center">
<sub>Runic Gateway · whitlocktech@gmail.com</sub>
</div>