Reconnaissance across the nine RunicGateway checkouts, and the plan for the public site + documentation experience built on top of them. The site is a presentation layer over existing project knowledge: normative contracts stay canonical in docs/ and are linked, never copied. What the site authors is the journey — install, configure, administer, extend — which no existing document owns end to end, because the repos are organised by component and an operator is not. Two things worth reading before the rest: - Section 3 lists eleven places the repositories contradict each other, verified against origin/main rather than resolved quietly. The sharpest is that link/INTEGRATION.md documents the protocol-4 event kinds while its normative section 2 still declares X-UOLink-Version: 3 — one document disagreeing with itself. installer/INSTALL.md still hands operators "Protocol version 3", which is the value they paste into Admin -> Shard, and its own troubleshooting table says a stale number looks exactly like the shard going offline. - Section 4 makes fixing those a Phase 0 that lands before the site quotes anything: four PRs across docs and .profile, none of which touch code or a contract. They correct documentation to match what main already does. Section 1 also records a process failure and the rule taken from it. An earlier draft claimed the platform was on protocol 3 with 4 "not cut over". Every checkout was on a feature branch whose local main ref had never been fetched, so git show main:<path> answered from a months-old blob. Every fact in section 2 is now read from origin/main or the Gitea API, and section 7 turns that into a build-time check instead of a promise. Section 9 leaves five questions open rather than assuming answers. Co-Authored-By: Claude <noreply@anthropic.com>
351 lines
21 KiB
Markdown
351 lines
21 KiB
Markdown
# runicgateway.com — design of record
|
||
|
||
**Status:** proposal, awaiting org-lead approval. Nothing is built.
|
||
**Date:** 2026-08-19
|
||
**Repo:** `RunicGateway/runicgateway.com`
|
||
**AI-assisted:** yes — drafted by Claude, per the org's AI-usage disclosure policy.
|
||
|
||
The public website and documentation site for Runic Gateway. Two audiences: **server
|
||
administrators** who want to understand and install the platform, and **developers** who want to
|
||
build modules and integrations for it.
|
||
|
||
This document is the plan. It is deliberately written to be argued with — §9 lists the questions
|
||
that are still open, and §2 lists the things the existing repos disagree about rather than picking
|
||
a winner quietly.
|
||
|
||
---
|
||
|
||
## Contents
|
||
|
||
1. [The principle](#1-the-principle)
|
||
2. [Verified platform state](#2-verified-platform-state)
|
||
3. [What the repositories disagree about](#3-what-the-repositories-disagree-about)
|
||
4. [Phase 0 — fix the documentation the site will quote](#4-phase-0--fix-the-documentation-the-site-will-quote)
|
||
5. [Information architecture](#5-information-architecture)
|
||
6. [Visual direction](#6-visual-direction)
|
||
7. [Accuracy machinery](#7-accuracy-machinery)
|
||
8. [Build phases](#8-build-phases)
|
||
9. [Open questions](#9-open-questions)
|
||
|
||
---
|
||
|
||
## 1. The principle
|
||
|
||
**The site is a presentation layer over existing project knowledge, never a competing source of
|
||
technical truth.**
|
||
|
||
Concretely:
|
||
|
||
- The normative contracts stay where they are and are **linked, never copied**:
|
||
[`MODULE_API.md`][api], [`MODULE_SYSTEM.md`][system], [`link/INTEGRATION.md`][int],
|
||
[`link/v3.md`][v3], [`link/v4.md`][v4], [`website/TEAMS.md`][teams],
|
||
[`website/BACKEND_DESIGN.md`][backend], [`installer/INSTALL.md`][install].
|
||
- The site authors the **journey** — install, configure, administer, extend — which is the thing no
|
||
existing document owns end to end, because the repos are organised by component and an operator
|
||
is not.
|
||
- Where marketing language and technical documentation conflict, **the technical documentation
|
||
wins**, and the marketing language changes.
|
||
- No feature is advertised that does not exist. §2 records what does; the "not built" list is as
|
||
load-bearing as the rest.
|
||
|
||
### A process rule, learned the hard way
|
||
|
||
An earlier draft of this report stated the platform was on **protocol 3**, with protocol 4
|
||
"feature-complete on `edge`, not cut over". That was wrong — protocol 4 is on `main` and released.
|
||
|
||
The cause: all nine checkouts in the workspace sat on feature branches whose local `main` ref had
|
||
**never been fetched**, so `git show main:<path>` answered confidently from a months-old blob. Local
|
||
`main` was 1–3 commits behind `origin/main` in seven of nine repos.
|
||
|
||
**Rule for this repo:** every version, protocol number or capability claim that reaches the website
|
||
is verified against `origin/<default-branch>` or the Gitea API *at the moment it is written*, never
|
||
against a local working tree. §7 makes that a build-time check rather than a promise, because a
|
||
promise is exactly what failed.
|
||
|
||
---
|
||
|
||
## 2. Verified platform state
|
||
|
||
All values read from `origin/main` or the Gitea API on 2026-08-19.
|
||
|
||
### Versions
|
||
|
||
| Component | Value | Authority |
|
||
|---|---|---|
|
||
| **Wire protocol** | **4** | `link/sidecar/src/main.rs` `PROTOCOL_VERSION`; `servuo-plugins/overlay.toml` `protocol = 4` |
|
||
| **Module API** | **1.6.0** | `website/server/src/modules/version.js` |
|
||
| **Current bundle** | **2026.08.19** (protocol 4) | `installer` branch `bundles`, `current.json` |
|
||
| uo-link sidecar | **v2.0.0** | release; in bundle 2026.08.19 |
|
||
| Plugin overlay | **v1.0.0** | release; in bundle 2026.08.19 |
|
||
| Installer | **v0.1.0** | release |
|
||
| `module-uo` | **v1.0.1** | release (`module.json` on `main` reads `0.3.0`; the release version is tag-derived) |
|
||
| Android app | **v0.5.0** | release |
|
||
| ServUO | **57.4** — the only version the patch tier is verified against | `overlay.toml` |
|
||
|
||
### What exists
|
||
|
||
Confirmed present on `main`, not inferred from design docs.
|
||
|
||
**Core (game-agnostic):** accounts and sessions; wiki; posts across four categories (news,
|
||
five-on-friday, newsletter, screenshots); CMS pages and a page builder; navigation editor;
|
||
appearance/theming; branding as data via `BRAND_*`; invites; moderation and appeals; content
|
||
reports; append-only activity log; bot-scoring with automatic IP bans; TOTP two-factor with trusted
|
||
devices and recovery codes; SSO over OAuth2/OIDC (Google, Discord, generic — **link-only**, never
|
||
auto-provisioned); mobile bearer auth with rotated hashed refresh tokens; push through self-hosted
|
||
**ntfy**; the module loader and the admin Modules screen; OpenAPI 3.0 at `/api/docs`.
|
||
|
||
**Teams** — merged to `main` (PR #161, cutover 4/6). Team core and roster; forums with
|
||
announcements, discussion threads, replies, an edit window, post moderation and member-raised abuse
|
||
reports; notifications across web, push and email; Discord slash commands; one voice channel per
|
||
Team, granted by role.
|
||
|
||
**`module-uo` (72 routes)** — capabilities `shard · atlas · market · governors · guilds · houses ·
|
||
champs · cliloc`: shard status, economy, houses and IDOC decay, character sheets, the player-vendor
|
||
marketplace, a spawn atlas built from the shard's own spawn tables, champion boards, points/loyalty
|
||
leaderboards, town crier, account linking, and the staff control consoles.
|
||
|
||
**The bridge** — ServUO plugin → loopback TCP `127.0.0.1:7788` (newline-delimited JSON, the shard
|
||
dials out) → uo-link sidecar (Rust, SQLite) → website over authenticated WebSocket + REST. The
|
||
installer deploys the shard side as a protocol-checked bundle and registers a service under an
|
||
unprivileged account.
|
||
|
||
### What does NOT exist — and will not be advertised
|
||
|
||
- **Matrix.** [`TEAMS.md`][teams] §8 is explicit research, not a roadmap item: Matrix has no
|
||
channel-with-overwrites, no role object, no voice channel (voice is a MatrixRTC session needing a
|
||
LiveKit SFU the homeserver does not ship), and no slash-command registration. Of the five
|
||
capabilities a shared interface would name, a Matrix implementation could honestly provide two.
|
||
The settled outcome was a **capability contract**, not an integration. The original brief for this
|
||
site listed Matrix as a feature; it is not one.
|
||
- **Multi-module deployments.** [`TEAMS.md`][teams] Part 1 puts this explicitly out of scope — one
|
||
active module per deployment. `module_id` columns exist to keep it later-friendly; nothing
|
||
exercises them.
|
||
- **A second game module.** `module-rust` is a paper dry-run (`docs/modules/rust-dryrun.md`),
|
||
deliberately unimplemented — it exists to test that the contract generalises.
|
||
- **A finished Integration Kit.** It describes itself as a draft: "finished when someone outside
|
||
this project builds a working module for a new game by following it alone. That has not happened
|
||
yet."
|
||
- **macOS and Windows-on-ARM installer builds.** Deliberately absent — the shard and sidecar must
|
||
share a host, and no ServUO host is either.
|
||
|
||
---
|
||
|
||
## 3. What the repositories disagree about
|
||
|
||
Verified against `origin/main`. None of these are caused by this project; all of them would be
|
||
**inherited and amplified** by a website that quotes them.
|
||
|
||
| # | Conflict | Location | Severity |
|
||
|---|---|---|---|
|
||
| 1 | **`INTEGRATION.md` contradicts itself.** Its event catalog fully documents the protocol-4 kinds — `guild.roster`, `guild.leave`, a subsection headed "On Protocol 4" — while its normative §2 still declares `X-UOLink-Version: 3`, `"protocol": 3` in `/health` and `ws.hello`, and a worked client example sending `"3"`. | `docs/link/INTEGRATION.md` lines 36, 58–63, 95, 118, 1015 | **High** — this is the contract a third-party integrator implements against |
|
||
| 2 | **`INSTALL.md` still tells operators protocol 3.** Line 413 is the "Protocol version 3" value in the four-value handoff an operator pastes into Admin → Shard. The document's own troubleshooting table says a stale number is answered with `409` and "looks exactly like your shard going offline". | `docs/installer/INSTALL.md` lines 159, 163, 164, 413, 516, 726, 862 | **High** — actively misconfigures a real install |
|
||
| 3 | **`v4.md` says the cutover has not happened.** "Status: Feature-complete on `edge`… `edge` → `main` is the v4 cutover" — but link v2.0.0 and overlay v1.0.0 are released from `main` and bundle 2026.08.19 pairs them at protocol 4. | `docs/link/v4.md` line 3 | Medium |
|
||
| 4 | **The org landing page prints protocol 3** in the installer's four-value handoff block. | `.profile/README.md` line 179 | Medium |
|
||
| 5 | **Stale component versions throughout `INSTALL.md`** — bundle `2026.08.04`, overlay `v0.1.1`, sidecar `1.1.0`, including in copy-pasteable `curl` commands in Appendix A. | `docs/installer/INSTALL.md` lines 119, 512, 522–523, 657–658, 705–706, 725, 862 | Medium |
|
||
| 6 | **Two architecture diagrams disagree.** `docs/website/ARCHITECTURE.md` places `shardIngest.js` and `uoLinkClient.js` *inside* the website backend; `website/README.md` places them in the module. ARCHITECTURE.md predates the module system. | `docs/website/ARCHITECTURE.md` | Medium |
|
||
| 7 | **`website-README.md` has drifted from the live README** — the snapshot lacks "Three ways in, and none of them is a build" (admin panel / `MODULES` env / by hand), which is now the primary module-install story. | `docs/website/website-README.md` | Low |
|
||
| 8 | **`docs/README.md`'s index is stale** — no entry for `TEAMS.md`, `ARCHITECTURE.md`, `TRUSTED_DEVICES_MFA.md` or `MODERATION_APPEALS.md`. | `docs/README.md` | Low |
|
||
| 9 | **`BACKEND_DESIGN.md` is titled "UOMysticmoon Website — Backend Design"** although core is game-agnostic and UO is a module. | `docs/website/BACKEND_DESIGN.md` | Low |
|
||
| 10 | **`Module-uo/README.md` shows Phase 4 as `⬜`** though the module system shipped 2026-08-12 and the admin Modules screen exists. | `Module-uo/README.md` line 41 | Low |
|
||
| 11 | **Product name spelling.** All prose is "Runic Gateway"; identifiers are `RunicGateway`. **Settled by the org lead: keep "Runic Gateway" in prose.** Recorded because the site's original brief mandated the opposite. | everywhere | Settled |
|
||
|
||
---
|
||
|
||
## 4. Phase 0 — fix the documentation the site will quote
|
||
|
||
Conflicts 1–5 are protocol-accuracy bugs that exist independently of this website, and the site
|
||
cannot be written honestly on top of them: "Connect a game server" has to tell an operator which
|
||
number to type into Admin → Shard, and today the canonical guide says the wrong one.
|
||
|
||
**Phase 0 lands before the site quotes anything.** Each is a separate PR in its own repo, using the
|
||
verified values from §2.
|
||
|
||
| PR | Repo | Change |
|
||
|---|---|---|
|
||
| 0.1 | `docs` | `link/INTEGRATION.md` — protocol 3 → 4 in §2 and every normative example (`X-UOLink-Version`, `/health`, `ws.hello`, the JS client). Resolves the self-contradiction with its own protocol-4 event catalog. |
|
||
| 0.2 | `docs` | `link/v4.md` — status line: the `edge` → `main` cutover is done; released as link v2.0.0 + overlay v1.0.0 in bundle 2026.08.19. |
|
||
| 0.3 | `docs` | `installer/INSTALL.md` — protocol 3 → 4 and the stale component versions → the current bundle, including Appendix A's `curl` commands. |
|
||
| 0.4 | `.profile` | `README.md` — protocol 3 → 4 in the handoff block. |
|
||
|
||
Conflicts 6–10 are documentation hygiene rather than operator-facing errors. They are **not**
|
||
blockers and are proposed as an optional follow-up batch — see §9, Q2.
|
||
|
||
**Nothing in Phase 0 changes code or a contract.** Each PR corrects documentation to match what the
|
||
code already does, and each cites the source of truth in its description.
|
||
|
||
---
|
||
|
||
## 5. Information architecture
|
||
|
||
Organised by what a reader is trying to do. A reader should never need to know that `link`,
|
||
`servuo-plugins` and `installer` are three repositories in order to connect a game server.
|
||
|
||
### Marketing pages
|
||
|
||
| Route | Purpose |
|
||
|---|---|
|
||
| `/` | Hero, the data path, grouped capabilities, the self-hosted argument, get-started CTA |
|
||
| `/features/` | Grouped capability presentation, explicit about what is core and what a module supplies |
|
||
| `/architecture/` | The system explained visually, for a technical evaluator deciding whether to run it |
|
||
| `/modules/` | What a module is, `module-uo` as the worked example, writing your own, the Integration Kit |
|
||
| `/integrations/` | Discord, mobile + ntfy push, SSO — with an explicit "not built" list |
|
||
|
||
**Feature grouping**, using project terminology:
|
||
|
||
- **Community** — Teams, Team forums, notifications, wiki, news and newsletter, player self-service
|
||
- **Game intelligence** *(module-supplied; `module-uo` today)* — shard status, economy, player-vendor
|
||
marketplace, houses and IDOCs, character sheets, spawn atlas, champion boards, points leaderboards
|
||
- **Administration** — roles, moderation and appeals, content reports, audit log, bot scoring and IP
|
||
bans, module management, the shard connection
|
||
- **Integration** — modules, the sidecar bridge, Discord (slash commands, notifications, voice),
|
||
mobile and push, SSO
|
||
- **Infrastructure** — self-hosted, Docker, prebuilt pull-only images, branding as data, OpenAPI
|
||
|
||
### Documentation
|
||
|
||
```
|
||
Getting started What is Runic Gateway? · Requirements · Install the site ·
|
||
First run · Install a game module · Connect a game server ·
|
||
Verify the whole stack
|
||
|
||
Administration Configuration · Branding and theming · Navigation and pages ·
|
||
Users and roles · Authentication · Teams · Moderation ·
|
||
Notifications and email · Managing modules ·
|
||
The shard connection · Maintenance and upgrades · Troubleshooting
|
||
|
||
Modules The module system · Installing modules · Module lifecycle ·
|
||
The module manifest · The module API · Building a module ·
|
||
The Integration Kit · Testing and release
|
||
|
||
Architecture System architecture · The bridge · Authentication architecture ·
|
||
Teams architecture · Protocol versions
|
||
|
||
Reference Environment variables · Installer CLI · sidecar.toml ·
|
||
Bridge.cfg · HTTP API · Event catalog · Canonical documents
|
||
```
|
||
|
||
Roughly 37 pages. Every Reference page is a **navigable summary plus a link to the canonical
|
||
document** — never a re-specification, per §1.
|
||
|
||
### The installation path
|
||
|
||
The repos treat these as separate deployments; this site is the first place they appear as one
|
||
sequence. Worth stating plainly, because it is a real trap: **a "Runic Gateway install" is two
|
||
independent installs.** The installer binary sets up the *shard side only* and never contacts the
|
||
website; the website is a separate Docker deployment.
|
||
|
||
1. Requirements
|
||
2. Install the site — Docker Compose, pull-only
|
||
3. First run — first admin, maintenance → live
|
||
4. Install a game module — admin panel, `MODULES` env, or by hand
|
||
5. Connect a game server — the installer binary on the shard host (ServUO-specific today)
|
||
6. Paste the four values into Admin → Shard
|
||
7. Verify — `[bridge status` in game, `/health` reporting `plugin_connected: true`, then `doctor`
|
||
8. Configure authentication and integrations
|
||
|
||
Each step states what the operator should expect to see, and links the failure modes to
|
||
Troubleshooting.
|
||
|
||
---
|
||
|
||
## 6. Visual direction
|
||
|
||
**"Modern infrastructure software with an arcane identity."** Dark-first. Marketing pages are
|
||
single-theme by design; the docs honour the reader's light/dark preference.
|
||
|
||
- **The palette descends from the product's own tokens** (`website/client/src/styles/theme.css`), so
|
||
the site and the thing it describes read as one family: near-black ground, a steel-blue accent
|
||
derived from the product's `#7f99bd`, and the product's existing live/maintenance signal colours.
|
||
One secondary — a muted violet — reserved **exclusively** for the runic motif (hairlines,
|
||
gradients, diagram glow) and never used for text.
|
||
- **Type:** Cinzel — already the project's display face — for the wordmark and hero only; a modern
|
||
variable sans for everything else. Self-hosted, so there are no external font requests.
|
||
- **Motif:** hand-drawn SVG geometry — a gateway glyph, thin luminous topology lines, layered
|
||
panels. Subtle, structural, used where it explains something. **No AI-generated rune artwork
|
||
anywhere.**
|
||
|
||
### On the existing emblem
|
||
|
||
`website/client/public/assets/img/runic-emblem.png` is an ornate gold-and-gems medallion. It reads
|
||
as instance branding for UOMysticmoon, and as exactly the fantasy-decoration register the site is
|
||
meant to avoid. **Recommendation: draw a new stroke-only geometric gateway mark** that scales down
|
||
to a favicon. See §9, Q3.
|
||
|
||
---
|
||
|
||
## 7. Accuracy machinery
|
||
|
||
The site quotes versions, protocol numbers and capability lists. Given §1's process rule, that needs
|
||
a mechanism rather than diligence:
|
||
|
||
- **`src/data/platform.json`** — one file holding every externally-sourced fact: protocol version,
|
||
Module API version, current bundle tag, component versions, `module-uo`'s capability list. Every
|
||
page reads from it. **No version number is ever hardcoded in prose.**
|
||
- **`scripts/checkFacts.mjs`** — fetches the authority for each fact (Gitea raw `overlay.toml`,
|
||
`version.js`, `bundles/current.json`, release tags) and **fails the build on any disagreement.**
|
||
Same mechanism and the same intent as the Integration Kit's `checkCoreApi.js`: when the platform
|
||
moves, this repo goes red so someone updates the site. That failure is the feature.
|
||
- **`scripts/checkLinks.mjs`** — every internal link resolves; every outbound link into a
|
||
`RunicGateway` repo points at a branch path, not a commit permalink.
|
||
- `astro check` plus a production build, in CI on every PR.
|
||
|
||
---
|
||
|
||
## 8. Build phases
|
||
|
||
| Phase | Deliverable |
|
||
|---|---|
|
||
| **0** | The documentation fixes in §4 — four PRs across `docs` and `.profile` |
|
||
| **1** | Foundation: scaffold, design tokens, typography, layout shell, shared header/footer, the SVG mark, docs theming, sidebar, `platform.json` + `checkFacts.mjs` |
|
||
| **2** | Homepage: hero, the data-path diagram as inline SVG, grouped capability sections, CTA |
|
||
| **3** | Marketing: `/features/`, `/architecture/`, `/modules/`, `/integrations/` |
|
||
| **4** | Docs — the journey: Getting started (7) + Administration (12). The installation path is the priority of the whole project |
|
||
| **5** | Docs — builder and reference: Modules (8) + Architecture (5) + Reference (7) |
|
||
| **6** | Polish: responsive, accessibility, SEO/OpenGraph/sitemap/robots, full-text search, screenshot components |
|
||
| **7** | Validation: `astro check`, production build, link check, fact check, mobile layout verified in a real browser |
|
||
| **8** | Delivery: Dockerfile (static build → static server), `docker-compose.yml`, Gitea Actions workflow, README, CONTRIBUTING with the AI-disclosure requirement |
|
||
|
||
**Stack:** Astro + Starlight — Astro for the marketing pages, Starlight for `/docs` (sidebar,
|
||
breadcrumbs, previous/next, automatic table of contents, offline full-text search). Static output,
|
||
Vite underneath, so it stays inside the org's existing tooling family.
|
||
|
||
**Deploy:** a Docker image behind the org lead's reverse proxy. This repo ships the image and the
|
||
compose file; the deployment endpoint is wired on the host.
|
||
|
||
---
|
||
|
||
## 9. Open questions
|
||
|
||
**Q1 — Does Phase 0 land as PRs from here, or as issues?**
|
||
§4 proposes four PRs correcting protocol and version references in `docs` and `.profile`. The
|
||
alternative is filing issues and letting the site launch alongside documentation that contradicts
|
||
it. *Recommendation: PRs — the docs are wrong regardless of whether this site is ever built.*
|
||
|
||
**Q2 — Do conflicts 6–10 get fixed too?**
|
||
Documentation hygiene, not operator-facing errors: the stale architecture diagram, the drifted
|
||
README snapshot, the stale docs index, the UOMysticmoon title, the `⬜` phase table. Cheap to fix,
|
||
outside the site's scope, and each is one small PR. Fix now, later, or never?
|
||
|
||
**Q3 — Screenshots.** There are **zero web-UI captures** anywhere in the workspace. The only real
|
||
product images are 14 Android screenshots in `docs/android/screenshots/`. Options: (a) stand up the
|
||
local review stack and capture real web screenshots, (b) ship clearly-labelled placeholder
|
||
components to fill later, (c) launch with the Android captures and diagrams only.
|
||
|
||
**Q4 — The product mark.** Draw a new geometric SVG gateway mark, or keep `runic-emblem.png`?
|
||
|
||
**Q5 — How direct should the docs be about maturity?** The Integration Kit calls itself a draft,
|
||
protocol 4 is days old, the installer is v0.1.0. The brief asks the site to look like an established
|
||
product. A "draft" badge on the Integration Kit pages is honest and costs little credibility; saying
|
||
nothing is more polished and less true. Preference?
|
||
|
||
---
|
||
|
||
[api]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/website/MODULE_API.md
|
||
[system]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/website/MODULE_SYSTEM.md
|
||
[int]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/INTEGRATION.md
|
||
[v3]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/v3.md
|
||
[v4]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/v4.md
|
||
[teams]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/website/TEAMS.md
|
||
[backend]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/website/BACKEND_DESIGN.md
|
||
[install]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/installer/INSTALL.md
|