Section 7 audited the plan against the Integration Kit and the template. This pass reads MODULE_API.md itself and enumerates every member rather than grepping for registration names. It found one regression, one mispriced decision, one missing declaration, and a set of ctx members the plan had never mentioned. THE REGRESSION. ctx.events.reconcile() and an action's reconcile() appear nowhere in the document. The twelve-phase first draft had them - phase 6, reconcile and the boot-id watch - and the rewrite to twenty phases lost them. That is a regression in the plan, not a decision. It matters MORE for Rust than for the game the contract was written against: the kit rates reconcile the one omission that is merely a lower standard rather than a broken promise, but that assumes a world which persists. Rust wipes monthly and a wipe invalidates every ledgered resource for that server at once, and core cannot tell a wedged sidecar from a game that rebooted and lost everything - it sees ok:false retry:true either way. It asks once at its own boot and otherwise waits to be told. ctx.events.reconcile() is being told, and the trigger is a watch on the game's boot id changing, which is also the only way to tell a game restart from a sidecar reconnect. Restored to phase 13. R9 WAS MISPRICED. It says the map's per-layer switches work through the existing visibility framework, which reads as reuse. It is not reuse: section 6.3 records shardVisibility as module-owned, and the tree confirms it - the util, both models, the admin controller and its tests all live under module-uo/server/ and there is nothing by that name left in website/server. Section 2.7 forbids a module requiring outside its own directory, so module-rust cannot import a line of it and builds its own. Not a reason to change the decision; phase 14 now carries a visibility layer of its own rather than a configuration of somebody else's. A MISSING DECLARATION. R13 claims two extension slots and never says where they are declared. module.json has an extensions array and the dry run's own manifest carried it. Like mounts it is a statement of surface the loader holds against reality, so phase 1 adds it. THE CTX MEMBERS. ctx has 29 members and the plan named a handful. The ones that change work: secretBox, because each configured server's sidecar token is a secret at rest and core already has the posture for it; middleware.rateLimit, which is the mechanism R1 asked for without naming; uploads, which is where R9's map image actually lands - the plan described fetching it and never said where it goes; activity.log, which both R2 and R18 owe an audit trail to; the three ctx.teams calls, since Teams is more than the provider; ctx.posts behind the announce leg and post hook; and the three send paths section 10's catalogue implies. Also records section 6.8, a constraint on phase 10 and on purge: a trigger, a rule and an audience outlive the module that declared them. engagement_rules.trigger_id is a plain VARCHAR with no foreign key and no cascade, so a module can be removed and reinstalled without destroying an operator's rules - and a rule whose trigger or audience is unregistered shows DORMANT, never an error and never auto-deleted. An audience resolving to the empty set is not the same answer as resolved to nobody and must not be rendered as if it were. The failure that prevents is exact: an id that stops resolving must never silently become a send to a different set of people. And the two client lists in full, since section 7 said UI kit discipline without saying what is in it; plus one confirmation for R10, that /api/v1/public/modules returns only started modules with no state and no failure reason, so a Rust module that failed to boot makes the app render a site without those screens rather than one advertising screens that 503. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
edge → main)' (#232) from edge into main
Runic Gateway — Documentation
Central documentation for the Runic Gateway platform. The docs here were extracted from the two code repositories (with full commit history preserved) so they live in one place, independent of either codebase.
Layout
website/ docs from the website core (Node/Express + MariaDB + React/Vite)
modules/ docs for installable game modules — one directory per module id
link/ docs from the ServUO bridge (C# plugin + Rust sidecar + Node WS)
android/ docs from the native Android client (Kotlin + Jetpack Compose)
installer/ docs for the installer that deploys a shard's bridge components
ci/ cross-cutting CI/quality notes
Setting up a shard? installer/INSTALL.md is the operator guide, and
the installer is the supported path: one binary deploys the plugin overlay, installs the uo-link
sidecar as a service, and hands you the values the website needs.
website/
| Doc | What it covers |
|---|---|
| BACKEND_DESIGN.md | API contract, DB schema, security model |
| ARCHITECTURE.md | The system diagram — how core, an installed module, the sidecar and the clients fit together |
| TEAMS.md | Teams as a platform primitive: roster, forums, notifications, Discord slash commands and voice — design of record |
| ENGAGEMENT.md | The engagement system: module-declared event triggers, rules, cooldowns, templates and the email / push / in-app delivery channels — design of record |
| HERO_EDITOR.md | Hero canvas editor feature spec |
| THEMING_AND_NAV.md | Admin-configurable theme, brand assets and navigation — build contract |
| MODULE_SYSTEM.md | Making the site game-agnostic: game logic becomes an installable module — design of record |
| MODULE_API.md | The module ↔ core contract: ctx, the register* calls, the client registry and the loader's obligations |
| UPGRADE_NOTES.md | Operator-facing, newest first — the upgrades that need an operator to do something, or that change behaviour quietly enough to be discovered by accident |
| WIKI_UPGRADE.md | Wiki subsystem upgrade notes |
| SHARD_VISIBILITY.md | Who sees which shard data — the admin-configurable audience framework |
| TRUSTED_DEVICES_MFA.md | TOTP two-factor, trusted devices and recovery codes |
| MODERATION_APPEALS.md | Moderation actions, content reports and the appeals flow |
| SPAWN_ATLAS.md | The bestiary / spawn atlas: what the shard contains, parsed from the shard's own ServUO files — served over the bridge since Protocol 8, so no shared filesystem |
| CLILOCS.md | UO's id → name table, so items have names. The shard decompresses and serves it over the bridge; the desktop conversion it replaced is gone |
| MARKETPLACE.md | The player-vendor index: how it is gathered, what it costs, how to tune it |
| website-README.md | Snapshot of the website repo's README (setup/run reference) |
| test-plan.md | The website's test strategy and harness |
| API_V2_PLAN.md | Router domain split + CSP hardening. The split is complete; only the CSP enforce step remains |
| API_V2_SKELETON.md | Superseded — the /api/v2 scaffold that was never built. Kept as the record of why the split was done in place instead |
| PROJECT_TREE.md | Auto-generated snapshot of the repo's tracked file layout |
modules/
Documentation for installable game modules aggregates here rather than in each module's repo (MODULE_SYSTEM.md §2.10). The website core knows nothing about any particular game; a module is what makes it a site for one.
| Doc | What it covers |
|---|---|
| uo/ | module-uo — the Ultima Online module: what it serves, what it owns, and what an operator needs |
| uo/API.md · uo/SCHEMA.md | module-uo's own route surface and the tables it owns |
| kit-acceptance.md | The Integration Kit acceptance run — building a module by following the kit alone, and what it found |
| rust-dryrun.md | A written, deliberately unimplemented module-rust — the test that the module contract generalises past the game it was extracted from |
| rust/ | Reference for the upcoming module-rust — a mirror of the uMod/Oxide ecosystem, scraped from upstream: HOOKS.md (477 Rust hooks), OXIDE_API.md (the plugin framework), DEFINITIONS.md (678 items, 2,590 skins), OPERATING.md (the operator's side), and agent/ — the same facts as TSV/JSONL at ~46% of the tokens |
link/
| Doc | What it covers |
|---|---|
| INTEGRATION.md | How the website integrates with the uo-link sidecar |
| PROTOCOL_2.md | Protocol 2.0 / 2.1 design |
| v3.md | Protocol 3.0 design — shard content/standings streams + the visibility framework |
| v4.md | Protocol 4.0 — guild membership on the wire (guild.roster, guild.leave) |
| v5.md | Protocol 5 — three enrichments in one bump: house.decay's decay schedule, vendor.listing's fee state, and account.login.result. Shipped 2026-09-01 as bundle 2026.09.01 (sidecar v2.1.0 + overlay v1.1.0) |
| v6.md | Protocol 6 — idempotent commands, config leases with a shard-side deadline, and the run-scoped participation ledger |
| v7.md | Protocol 7 — the world verbs an event OWNS: creatures, bosses, oracle NPCs, temporary gates, decoration, and the persisted ownership registry behind them. The released protocol, bundle 2026.09.10 (sidecar v2.2.0 + overlay v1.2.0) |
| v8.md | Protocol 8 — the Asset Bridge: the shard reads its own UO client and serves creature art, item and land pictures, the cliloc table and its own spawn files, so nothing is converted on a desktop and the website needs no shared filesystem. On edge |
| ADMIN_CONTROLS.md | Staff write-plane (kick/ban/broadcast, page queue) |
| SHARD_PREREQS.md | Shard-side prerequisites for the bridge |
| PLAN.md | uo-link build plan |
| RESEARCH.md | Research notes |
| link-README.md | Snapshot of the link repo's README |
| PROJECT_TREE.md | Auto-generated snapshot of the repo's tracked file layout |
android/
| Doc | What it covers |
|---|---|
| PLAN.md | Android client build plan / milestones |
| COVERAGE_PLAN.md | Test-coverage rollout plan |
| APP_LINKS.md | Android App Links / deep-link setup |
| theme-plan.md | Theming plan |
| THEMING_AND_NAV.md | The app's half of admin-configurable theming and navigation — build contract |
| TRUSTED_DEVICES_APP_HANDOFF.md | Trusted-devices app handoff notes |
| PROJECT_TREE.md | Auto-generated snapshot of the repo's tracked file layout |
installer/
| Doc | What it covers |
|---|---|
| INSTALL.md | Start here to set up a shard — the installer deploys the plugin overlay and the uo-link sidecar, registers the service, and connects it to the website. Appendix A is the same thing by hand, still supported |
| PLAN.md | Installer design of record — phases, locked decisions, the bundle/compat-matrix model |
| PROJECT_TREE.md | Auto-generated snapshot of the repo's tracked file layout |
ci/
| Doc | What it covers |
|---|---|
| SONARQUBE.md | The SonarQube setup: project keys, how analysis runs, and how to read a report |
Provenance
website/*was extracted fromRunicGateway/websiteviagit filter-repo.link/*was extracted fromRunicGateway/linkviagit filter-repo.
Commit history and authorship for each doc are preserved. The two source repos retain a short pointer to this repo in their own READMEs; the authoritative copy of each document now lives here.
License
Runic Gateway's documentation is free: licensed under the GNU General Public License v3.0 or later — see LICENSE.md.
Copyright (C) 2026 Runic Gateway
This documentation is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY. You may redistribute and/or modify it under the terms of
the GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
Contributions are welcome — please read CONTRIBUTING.md (note the AI-usage disclosure requirement) and our Code of Conduct.