How it is built

The parts, and the lines between them

Three boundaries decide almost everything about how this software behaves: the one between your two machines, the one between what the public sees and what staff see, and the one between the platform and the game. Each is drawn below, with the reasoning rather than the reference.

Nothing here is a specification. Where a real one exists it is linked — the protocol, the module contract and the operator guide are all documents in the open, and they are the authority when this page and one of them disagree.

What you deploy

Two hosts, two installs

Almost everyone gets this wrong once. The website and the game-side bridge are separate deployments on separate machines, and neither one installs the other — so a "Runic Gateway install" is really two, done in that order.

Today the game server is a ServUO shard and the sidecar is uo-link. Two machines is the minimum and also the maximum — nothing here scales by adding a third.

The web host

A Docker Compose deployment: the site, its database, and whichever game module you installed. Images are pulled rather than built, so nothing compiles here and an upgrade is a pull and a restart. This is the only machine anybody points a browser at, and the only one that needs a certificate.

The game host

The machine your game server is already on. One installer binary puts the plugin into the server's tree, installs the sidecar beside it and registers the service — then prints four values. It never contacts your website; you paste those four values into the admin panel yourself, and that is the moment the two halves meet.

Why they share a host

The game talks to the sidecar over loopback, on the same machine, and dialsout to do it. That is what lets the game server open no port at all — and it is also why there is no macOS installer build. The pair has to sit together, and no game server anybody runs is on one.

What crosses between them

One authenticated connection, in both directions: a WebSocket carrying the live feed up, and REST calls going down for point-in-time questions. Nothing else on either machine talks to the other, and the sidecar answers your site and nobody else.

What reaches the public

One feed in, two feeds out

A live game world emits things that are fine on a front page and things that are not: who logged in from which address, what the cheat detector flagged, what a staff member did to whom. Both arrive on the same connection, so something has to divide them.

The allowlist is the security boundary. A new kind of event is invisible to the public until somebody adds it, which is the safe direction to fail in.

It is an allowlist, not a blocklist

The public stream carries the kinds of event that are named as safe; everything else goes to the staff stream by default. That ordering is the whole point. A blocklist fails open — the day the game emits something new, it is already published — and an allowlist fails closed, so the worst case is a page that is missing something rather than a page that has published an address.

The decision lives on your server

Not in the sidecar and not in the game. The bridge is a deliberately dumb forwarder: it moves what the game emits and makes no judgements about audience. Everything about who may see what is decided by the site you run, in one place, where you can read it — and where changing it does not mean redeploying anything on the game host.

More than two audiences, in practice

Two streams is the transport. Above it sits a configurable audience model — logged out, signed in, linked to a game account, staff — that decides how much of a given surface each of those sees. The public stream is the floor of that, and it is the one that is a boundary rather than a setting.

When the game is down

Nothing arrives, and the site carries on. Live surfaces say the server is offline and everything that does not depend on it — the wiki, the news, accounts, the forums — is unaffected. A site that goes down with the game it reports on is not much of a status page.

Where the game stops

A seam, with a version on it

The core site does not know what a shard is, what a guild is, or that Ultima Online exists. Everything that does lives in an installable module on the other side of a declared interface — which is what makes "put your game on it" a shape rather than a slogan.

A module declares which versions of the interface it speaks. If that does not match what the site offers, it refuses to load and the site comes up without it.

The module brings its own everything

Not just screens: its routes, its database tables, its navigation rows, its slice of the OpenAPI spec and its own prebuilt client bundle. Installing it is a paste in the admin panel or a line in your environment — never a build step, because production runs an image you pulled, and an operator who has to compile something has been handed a maintenance job rather than a feature.

Failure is contained by design

A module that will not load is marked as failed and the site starts without it. Disabling one is a kill switch, not a visibility flag — its routes stop answering and its live connections close. Uninstalling keeps the data, and destroying the data is a separate, deliberate choice made in its own dialog.

Teams is the shape of the contract

Core owns the Teams primitive — the roster, the forum, the notifications, the voice channel — and does not own the word. A Team cannot be created in core at all; it arrives from the module, which is why the UO module calls them guilds and builds those pages itself. That is the pattern the whole interface is built on: core supplies the machinery, the module supplies the meaning.

What this does not yet prove

One module exists and it is Ultima Online. A second, for a different game, is a written dry-run that was deliberately never implemented — it exists to test whether the contract generalises on paper. Until somebody builds the second one, the seam is a well-argued design rather than a demonstrated one, and this site says so wherever it comes up.

Going deeper

The documents this page is a summary of

Everything above is an argument about shapes. These are the things that specify them, and they are what a module author, an integrator or an operator should be reading.

  • The bridge protocolWhat the game and the sidecar say to each other, and what the sidecar publishes. Protocol 7 today, and versioned so a mismatched pair is refused rather than misread.
  • The module contractThe normative interface between core and a module — currently1.10.0. This is the document that decides whether your module loads.
  • The operator guideSetting the game side up end to end, including the failure modes and what each step should look like when it worked.
  • The documentation on this siteThe same ground as a guided path rather than a specification, starting from an empty server.