First commit for the UO module — the game-specific half of the Runic Gateway
website, extracted from core so that core can become game-agnostic. Phase 0 of
the module system plan (docs/website/MODULE_SYSTEM.md §2.7) calls for this repo
to get its initial commit before any module work starts.
Governance scaffolding only; no module code. The design of record settles the
API surface in Phase 1 and Phase 3 is what fills the repo, so writing module
code now would be writing against a contract that does not exist yet.
- README.md what module-uo is, the phase table, the packaging layout,
and why an operator never builds anything
- CONTRIBUTING.md planning status, the dev loop (a module is not runnable on
its own), the zero-internal-imports and one-path-segment
rules, schema fragments instead of migrations
- SECURITY.md private reporting, plus the module-specific notes: the
module boundary is not a security boundary, access control
lives in core, and the uo-link token stays write-only
- CODE_OF_CONDUCT.md, CONTRIBUTORS.md, LICENSE.md (GPL-3.0-or-later),
PR + issue templates — the same set every repo in the org carries
- .gitignore Node-shaped; client/dist/ is ignored deliberately, since it
is a release artifact built by CI, not a source artifact
CI lands next, in a PR, mirroring how the installer repo was bootstrapped: an
empty repo cannot take a pull request, but everything after it can.
Co-Authored-By: Claude <noreply@anthropic.com>
6.2 KiB
Contributing to Runic Gateway — module-uo
Thanks for your interest in contributing! This repo is module-uo: the Ultima Online module for the Runic Gateway website. Everything the site knows about a shard — shard status, the spawn atlas, the marketplace, governors, clilocs, the town crier and the uo-link integration — lives here rather than in core, so that core stays game-agnostic and a second game can be a second module.
It is one repo, one bundle: the server half (routers, controllers, models, a schema fragment) and the client half (SPA screens, nav registrations, a feature provider) version together and release as a single artifact.
By participating you agree to abide by our Code of Conduct.
Status: planning
No module code exists yet. The design of record is
website/MODULE_SYSTEM.md
in the docs repo — read it before opening a PR here. It defines the module API
surface, the packaging layout, the state machine, the install/uninstall/purge
model, and the phases. Phase 1 has to settle the contract
(docs/website/MODULE_API.md) before module code can be written against it, and
Phase 3 is what fills this repo by extracting the UO half of website/.
If you want to change what a module can do — the API surface, the loader, the delivery model — propose it against the plan in the docs repo first. If you want to change what module-uo does with that API, a PR here is the right place.
Ways to contribute
- Report a bug or request a feature through the issue tracker (issue templates are provided).
- Improve the code or docs by opening a pull request (see below).
- Never report a security vulnerability in a public issue — see SECURITY.md.
Development setup
Prerequisites: Node 20+, and a checkout of
RunicGateway/website to
run the module against — a module is not runnable on its own. Core loads modules
synchronously from the filesystem at boot, so a working tree of this repo placed
in core's modules directory is the development loop.
Once the code exists, both halves are plain npm packages:
npm ci --prefix server && npm test --prefix server
npm ci --prefix client && npm run build --prefix client
The client half builds with Vite in library mode, declaring react,
react-dom and react-router-dom as externals. Do not add React to the module's
bundle: there is exactly one React instance in the page and core owns it, exposed
on a window.__rg global that the module's externals resolve to.
Two rules that are enforced in CI, not by review
- Zero internal-file imports into core. A module reaches core only through the
documented API surface — the
ctxit is handed and theregister*functions it calls. If something you need is not on that surface, the surface gets extended (a docs PR and a core PR) rather than reached around. Anyrequire/importthat resolves outside this repo is a build failure. - The module owns one path segment. Server routes keep the API paths core
already publishes; SPA pages live under
/uo/*,/admin/uo/*and/player/uo/*. A route that escapes the module's segment is a bug.
Schema fragments, not migrations
There is no migration runner anywhere in this project, and modules do not get one.
server/db/schema.sql is an idempotent fragment — CREATE TABLE IF NOT EXISTS, ALTER TABLE … ADD COLUMN IF NOT EXISTS — replayed by core's
ensureSchema() on every boot, exactly like core's own schema. Anything
destructive belongs in server/db/purge.sql, which only ever runs on an explicit
operator purge.
The operator never builds anything
Installing a module is an admin-panel action or a mounted directory, never a build
step: production runs a prebuilt, pull-only image. client/dist/ is produced by
this repo's CI and published in the release artifact. A change that only works
when someone builds on the server is not shippable.
Branch & PR workflow
- Branch from
mainwith a descriptive name (feature/…,fix/…,docs/…,chore/…). - Keep changes focused; small PRs are easier to review.
- Push and open a pull request against
main. Fill out the PR template, including the AI-assisted contributions disclosure. - A maintainer will review; address feedback with follow-up commits.
Module-uo develops on its own main from its first commit — it has no edge
branch and no cutover, unlike website, whose module work accumulates on edge
and reaches main once.
Commit messages
We use Conventional Commits —
type(scope): summary.
Keep the docs in sync
RunicGateway/docs is canonical, and this module's documentation lives there under
modules/uo/ rather than in this repo. A behavior change here — a new route, a
changed table, a new nav entry, a different capability — is not complete until the
matching document reflects it. Changes to the module system itself belong in
website/MODULE_SYSTEM.md and website/MODULE_API.md.
AI-assisted contributions (disclosure required)
This project is developed openly with AI assistance, and we ask the same transparency of everyone. If you used an AI tool (Claude, Copilot, ChatGPT, Cursor, etc.) to help produce a contribution, you must disclose it:
- Tick the AI-usage box in the pull-request template and name the tool(s).
- Mark AI-authored commits with a trailer, e.g.
Co-Authored-By: Claude <noreply@anthropic.com>orAssisted-By: <tool>. - You remain responsible for every line you submit: review it, understand it, and make sure it is correct and that you have the right to contribute it.
Disclosed AI assistance is welcome. Undisclosed AI-generated contributions are not, and may be closed.
License
Runic Gateway is licensed under the GNU General Public License v3.0 or later (see LICENSE.md). By submitting a contribution you agree that it is licensed under the same terms (inbound = outbound) and that you have the right to contribute it.