From 96f77a87e104a772b9e3ae8b5f10fdba7b694f0b Mon Sep 17 00:00:00 2001 From: wtclaude Date: Wed, 19 Aug 2026 15:07:30 -0500 Subject: [PATCH] docs(plan): the design of record for runicgateway.com MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: 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 --- PLAN.md | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 PLAN.md diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..3969adb --- /dev/null +++ b/PLAN.md @@ -0,0 +1,350 @@ +# 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:` 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/` 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 -- 2.49.1