From faf98141b72876ab08a336d73d3aae6a8cb29e5a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Jul 2026 16:55:20 -0500 Subject: [PATCH] docs(website): rebrand website-README to Runic Gateway MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sync the extracted website README with the current repo README after the org rename + BRAND_* branding work: UOMysticmoon → Runic Gateway as the default identity (UOMysticmoon retained only as the example first instance), UOM/link → RunicGateway/link, DB/cookie/TOTP defaults updated, and the new Branding section added. Doc-local BACKEND_DESIGN.md links kept relative; the .env.uomysticmoon.example link points at the website repo where it lives. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01XmHdsbnLzDMAVQkAoTQSBe --- website/website-README.md | 54 ++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/website/website-README.md b/website/website-README.md index 5f064b0..f6ac75d 100644 --- a/website/website-README.md +++ b/website/website-README.md @@ -1,12 +1,15 @@ -# UOMysticmoon Website +# Runic Gateway Website -Public site, wiki, and protected admin panel for the **UOMysticmoon** private Ultima Online -shard — a full-stack app in one repo: +Public site, wiki, and protected admin panel for a private Ultima Online shard — a +full-stack app in one repo. Branding is instance-configurable via `BRAND_*` (see +[Branding](#branding)); **UOMysticmoon** is the first instance. + +A full-stack app in one repo: - **Backend** — Node.js + Express REST API (layered `router → controller → model → db`), MariaDB, a provider-agnostic session layer (JWT cookie for web, bearer tokens for mobile, pluggable SSO). - **Frontend** — React + Vite single-page app (public site, wiki, and the admin panel), dark "gothic" theme (Cinzel + Georgia). - **Deploy** — Docker Compose (app + MariaDB) behind a Pangolin reverse proxy. Express serves the built SPA in production. -- **Shard link** — a live bridge to the in-game ServUO shard through the **uo-link** sidecar ([UOM/link](https://gitea.whitlocktech.com/UOM/link)): the site ingests a live event feed and makes server-side REST calls to show shard status, economy, staff presence, IDOCs, live activity, and per-character sheets. See [Shard integration (uo-link)](#shard-integration-uo-link). +- **Shard link** — a live bridge to the in-game ServUO shard through the **uo-link** sidecar ([RunicGateway/link](https://gitea.whitlocktech.com/RunicGateway/link)): the site ingests a live event feed and makes server-side REST calls to show shard status, economy, staff presence, IDOCs, live activity, and per-character sheets. See [Shard integration (uo-link)](#shard-integration-uo-link). The design reference is [BACKEND_DESIGN.md](BACKEND_DESIGN.md) (API contract, schema, security). @@ -133,14 +136,14 @@ to the backend, so the SPA stays same-origin (cookies work). **1. Start a MariaDB the backend can reach** (published on `localhost:3306`): ```bash -docker run -d --name uomm-db -p 3306:3306 -e MARIADB_DATABASE=uomysticmoon -e MARIADB_USER=uomm -e MARIADB_PASSWORD=devpass -e MARIADB_ROOT_PASSWORD=rootpass mariadb:11 +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 ``` **2. Configure + start the backend** (terminal 1): ```bash cp server/.env.example server/.env -# Set DB_HOST=127.0.0.1, DB_PORT=3306, DB_USER=uomm, DB_PASSWORD=devpass, +# Set DB_HOST=127.0.0.1, DB_PORT=3306, DB_USER=runic, DB_PASSWORD=devpass, # JWT_SECRET=, ADMIN_USERNAME=admin, ADMIN_PASSWORD= npm run install-server npm run server # nodemon → http://localhost:3000 @@ -249,7 +252,7 @@ actually returns (`400` validation, `401`/`403` auth, `404`, `409` conflicts, `4 **Authentication in the UI** — click **Authorize** and provide either: -- `cookieAuth` — the `uomm_token` session cookie (set automatically in the browser after +- `cookieAuth` — the session cookie (name `rg_token`, configurable via `COOKIE_NAME`; set automatically in the browser after `POST /api/v1/auth/login`), or - `bearerAuth` — a mobile access token from `POST /api/v1/auth/mobile/login` (sent as `Authorization: Bearer `). @@ -274,7 +277,7 @@ not crash). The site is wired to the live in-game world through **uo-link**, a standalone sidecar service that runs next to the ServUO shard. Its source lives in a separate repo: -**[UOM/link](https://gitea.whitlocktech.com/UOM/link)**. uo-link speaks the shard's internals and +**[RunicGateway/link](https://gitea.whitlocktech.com/RunicGateway/link)**. uo-link speaks the shard's internals and exposes a small, authenticated HTTP + WebSocket API; this website is a *client* of it. The shard itself is never exposed to the internet — only the sidecar is, and only the website's backend talks to it. @@ -282,7 +285,7 @@ to it. ### How it works ``` -ServUO shard ──▶ uo-link sidecar (UOM/link) ──▶ website backend ──▶ browser +ServUO shard ──▶ uo-link sidecar (RunicGateway/link) ──▶ website backend ──▶ browser REST + WebSocket, bearer-auth ingest + REST same-origin JSON/SSE ``` @@ -348,19 +351,20 @@ Copy `.env.example` (Compose) or `server/.env.example` (local) and fill in. **`. | `PORT` | `3000` | server listens on `0.0.0.0:PORT` | | `UPLOAD_DIR` | `/uploads` | where post images are written (`/app/uploads`, volume-mounted, in Compose) | | `DB_HOST` / `DB_PORT` | `db` / `3306` | `db` in Compose; `127.0.0.1` for local dev | -| `DB_NAME` / `DB_USER` / `DB_PASSWORD` | `uomysticmoon` / `uomm` / — | app database credentials | +| `DB_NAME` / `DB_USER` / `DB_PASSWORD` | `runic_gateway` / `runic` / — | app database credentials | | `DB_ROOT_PASSWORD` | — | MariaDB root (Compose only) | | `JWT_SECRET` | — | **required** — long random string; signs session, mobile, and SSO-flow tokens | | `JWT_EXPIRES_IN` | `1d` | web session token + cookie lifetime | | `COOKIE_SECURE` | `auto` | `auto` = Secure only over HTTPS (works on LAN HTTP + Pangolin HTTPS) | -| `COOKIE_NAME` | `uomm_token` | | +| `COOKIE_NAME` | `rg_token` | changing it on a live instance invalidates existing sessions | +| `BRAND_*` | Runic Gateway | instance branding (name, tagline, colors, logo/hero/favicon) — see [Branding](#branding) | | `SECRET_ENC_KEY` | — | **required in prod** — key for AES-256-GCM encryption of stored OAuth client secrets. Dev falls back to a key derived from `JWT_SECRET` (with a warning) | | `APP_BASE_URL` | — | public base URL, used to build the SSO OAuth `redirect_uri` (`${APP_BASE_URL}/api/v1/auth/sso/:provider/callback`). Set in prod to match what you register with Google/Discord; if unset it is derived from the request (fine for local dev) | | `MOBILE_ACCESS_TTL` | `15m` | mobile bearer **access** token lifetime (short-lived) | | `MOBILE_REFRESH_TTL_DAYS` | `30` | mobile **refresh** token lifetime (long-lived, rotated on use) | | `TRUST_PROXY` | `1` | reverse-proxy trust for correct `req.ip` / `req.secure` (rate limiting, backoff, bot-ban). Pin to the proxy hop's LAN IP in prod. A blanket `true` is rejected (coerced to `1`) to block `X-Forwarded-For` spoofing | | `DEBUG_TRUST_PROXY` | `0` | `1` logs raw peer address + `X-Forwarded-For` + resolved `req.ip` per request (to verify/refresh the proxy IP). Noisy — leave off | -| `TOTP_ISSUER` | `UOMysticmoon` | label shown in authenticator apps for optional per-user 2FA | +| `TOTP_ISSUER` | `BRAND_NAME` | label shown in authenticator apps for optional per-user 2FA | | `TOTP_CHALLENGE_TTL` | `5m` | lifetime of the short-lived post-password "awaiting code" step | | `ADMIN_USERNAME` / `ADMIN_PASSWORD` | — | first-admin bootstrap (first boot only) | | _Email_ | — | configured in Admin → Settings → Email (Gmail OAuth2), not via env; recipient = `contact_email` setting | @@ -372,6 +376,32 @@ Copy `.env.example` (Compose) or `server/.env.example` (local) and fill in. **`. --- +## Branding + +Instance identity is data, not code — set via `BRAND_*` env vars, so one prebuilt +image can run as any shard. With none set, everything renders as **Runic Gateway**. + +| Var | What | +|---|---| +| `BRAND_NAME` / `BRAND_SHORT_NAME` | display name (full / short-in-prose) | +| `BRAND_TAGLINE` / `BRAND_DESCRIPTION` | tagline + meta/OG description | +| `BRAND_CONTACT_EMAIL` / `BRAND_URL` | contact + canonical URL (for OG/absolute links) | +| `BRAND_ACCENT_COLOR` | theme `--accent` (web) + Discord embed color | +| `BRAND_LOGO` / `BRAND_HERO` / `BRAND_FAVICON` | image paths under the `/brand` mount, or absolute URLs | + +**How it flows:** text/colors reach the SPA at runtime through the public settings +API (`SiteContext`), so no rebuild is needed; the server templates `index.html` +``/meta/OG/favicon at boot; emails, TOTP issuer, and the Discord bot read +`BRAND_*` directly. The admin-editable **site title** and **contact email** +settings override `BRAND_NAME` / `BRAND_CONTACT_EMAIL` when set. Image assets are +delivered from the `./brand` bind-mount (see `brand/README.md`). + +**UOMysticmoon** is the first instance — [`.env.uomysticmoon.example`](https://gitea.whitlocktech.com/RunicGateway/website/src/branch/main/.env.uomysticmoon.example) +holds the exact `BRAND_*` + infra (`DB_NAME`/`DB_USER`/`COOKIE_NAME`) pinning to +run this repo as UOMysticmoon. + +--- + ## Security **Session & authorization**