Phase 4 slice 4. BACKEND_DESIGN.md had never been de-UO'd: Phase 3 rewrote core's code and README, but §5.2's identifier check reads code, not prose, so nothing ever looked at the design document. It still described 27 shard_* tables, 20 UO route rows and the shard visibility ladder as core's, a phase after core stopped being able to serve any of them. Moved, text unchanged: BACKEND_DESIGN §3 six shard_* schema sections (226 lines) → modules/uo/SCHEMA.md BACKEND_DESIGN §4 13 public + 7 admin UO route rows → modules/uo/API.md BACKEND_DESIGN §6.5 the audience ladder (70 lines) → modules/uo/API.md §4 Core keeps the seam and gains the eight /admin/modules routes it had never documented. §6.5 becomes "Module-owned audience boundaries": core's security boundary ends at authentication, roles and the session, and a module that serves game data brings its own. Also fixed on the way: users.router.js was still listed as 15 routes (it is 9 — six went to the extension slot), and the push section still promised config/shardStreams.js "moves out with it" four slices after it left. The acceptance table now carries results. Criterion 2 was proved for real against module-uo v0.3.0 on an empty database, which turned up the uninstall ordering defect fixed in website#146. AI disclosure: this contribution was AI-assisted (Claude Code). Co-Authored-By: Claude <noreply@anthropic.com>
161 lines
9.1 KiB
Markdown
161 lines
9.1 KiB
Markdown
# Shard visibility — who sees which shard data
|
|
|
|
**Status:** Built (Protocol 3.0 Part A). Admin → Shard Visibility.
|
|
**Audience:** shard owners and admins.
|
|
**Companion to** [`../link/v3.md`](../link/v3.md) §3 (the design) and
|
|
[`../modules/uo/API.md`](../modules/uo/API.md) §4 (the enforcement contract — it moved there with the
|
|
rest of module-uo's surface; core's [`BACKEND_DESIGN.md`](BACKEND_DESIGN.md) §6 keeps the security
|
|
contract core still owns).
|
|
|
|
The website surfaces a lot of live shard data. What your players, your staff and the anonymous
|
|
internet may each see is **yours to decide**, per feature, from Admin → Shard Visibility.
|
|
|
|
Nothing changes until you change it: every setting ships at the value that reproduces how the site
|
|
behaved before this panel existed.
|
|
|
|
---
|
|
|
|
## 1. The audience ladder
|
|
|
|
Five rungs. Each one includes everyone below it.
|
|
|
|
| Rung | In the UI | Who that is |
|
|
|---|---|---|
|
|
| `anonymous` | **Everyone** | Anyone at all, signed in or not. |
|
|
| `logged_in` | **Signed in** | Any registered account, whether or not they've linked a game account. |
|
|
| `player` | **Linked players** | Accounts with a linked in-game account. **Staff always qualify**, linked or not. |
|
|
| `staff` | **Staff** | Admins and moderators. |
|
|
| `admin` | **Admins only** | Admins. |
|
|
|
|
Two notes that surprise people:
|
|
|
|
- **`editor` is a content role, not a shard role.** Editors write news and wiki pages; they get no
|
|
shard privilege from that. An editor is treated by link status like any other member. This matches
|
|
the rest of the site, where shard staff powers are admin-or-moderator.
|
|
- **Staff satisfy `player` without linking.** Otherwise an admin would be locked out of surfaces
|
|
they'd gated to players, which is how the `/player/*` routes already behave.
|
|
|
|
## 2. What you can set per feature
|
|
|
|
**Enabled.** Off means gone. The feature's pages return “not found”, not “forbidden” — a disabled
|
|
feature doesn't advertise that it exists.
|
|
|
|
**Who can see it.** The minimum rung, from the ladder above.
|
|
|
|
**Live updates.** Whether this feature pushes changes to open pages in real time. Turning it off
|
|
doesn't break the page; it just refreshes on load instead of updating in place.
|
|
|
|
**Sensitive fields.** Some features expose a field that deserves its own rung — you can publish the
|
|
board while holding back one column. See the table in §3.
|
|
|
|
## 3. The features, and their defaults
|
|
|
|
| Feature | What it exposes | Default | Sensitive fields |
|
|
|---|---|---|---|
|
|
| **Shard status** | Connection state, online count, gold-supply series | Everyone | — |
|
|
| **Activity feed** | Deaths, kills, skill gains, quests, logins | Everyone | — |
|
|
| **Champion spawns** | The live champion / mini-champ / sea-boss board | Everyone | — |
|
|
| **Guilds** | Guild rosters, alliances, leaders | Everyone | — |
|
|
| **Town governors** | City Loyalty governors, elections, term history | Everyone | — |
|
|
| **Houses / IDOC** | Houses in danger | Everyone | House owner → Staff · House price → Staff |
|
|
| **Players online** | Population aggregate, staff-online widget | Everyone | In-game location → Staff |
|
|
| **Shard rules** | Skill/stat caps, house limits, vet rewards, the ruleset | Everyone | Connect address → Everyone |
|
|
| **Spawn atlas** | Bestiary and spawn locations (static content) | Everyone | — |
|
|
| **Leaderboards** | Point and loyalty standings | Everyone | Character names → Everyone |
|
|
| **Marketplace** | The shard-wide player-vendor index | Everyone, **live updates off** | Vendor owner name → Everyone · Vendor owner character id → Everyone · In-game location → Everyone |
|
|
|
|
**Why the marketplace ships with live updates off.** A live feed of every vendor's full inventory
|
|
would be the single largest thing the site sends. No page needs it — the marketplace is a search over
|
|
stored data with a “prices last refreshed N minutes ago” stamp. Turn it on only if you want it.
|
|
|
|
**Why the marketplace's fields default to Everyone.** A vendor's shop name, its owner's character
|
|
name and where it is standing are *already* visible to every player in game: the stock Vendor Search
|
|
gump surfaces exactly that set to anyone who opens it. Publishing them on the site is not a new
|
|
disclosure. They stay configurable because a shard may still prefer to keep its economy behind a
|
|
login — and because "already public in game" is a judgement about your shard, not ours.
|
|
|
|
**Location is one setting covering four things.** Hiding it removes the facet, the coordinates, the
|
|
region *and* the house name together. That is deliberate: those are four ways of saying the same
|
|
thing, and a setting that hid the coordinates while publishing the house name would not have hidden
|
|
anything.
|
|
|
|
**Hiding the owner name also hides the owner character id.** They are separate settings so you can
|
|
be explicit, but leaving the id published while hiding the name achieves nothing — the leaderboards
|
|
and guild boards resolve that same id back to a character name. Set both.
|
|
|
|
**What hiding a vendor cannot do.** Only vendors whose owner left the in-game *Vendor Search* flag ON
|
|
are ever sent to the site, so a player who hides their shop in game is hidden here too — and no
|
|
setting on this page can override that. It works the other way as well: these settings control who
|
|
sees the index, not whether players can find each other's shops in game.
|
|
|
|
**Why house owner/price default to Staff.** The public Houses page has always been a "where are the
|
|
falling houses" board — location only. Owner and price are the staff view. That split is preserved.
|
|
|
|
## 4. What you cannot change
|
|
|
|
Two rules are enforced in code and are not settings. Attempting to set them returns an error rather
|
|
than silently ignoring you.
|
|
|
|
**1. Game account names and website user ids are admin-only, always.**
|
|
`acct` and `webId` never appear below the admin rung on any surface. A character *name* is visible in
|
|
game to anyone standing next to them; the **account** behind it is not, and neither is the website
|
|
user it's linked to. Publishing those would disclose something the shard itself doesn't, and would
|
|
tie a player's in-game identity to their forum identity without their consent.
|
|
|
|
This rule matches the *meaning* of a field, not one spelling of it. Some responses nest the player
|
|
who owns a record (`leader.acct`); others flatten it into the row (`ownerAcct`, `leaderWebId`,
|
|
`governorAcct`). Every one of those is locked, and the admin API refuses to configure any of them —
|
|
so a new response shape can't quietly reopen the hole by naming the field differently.
|
|
|
|
**2. Unknown event kinds are never broadcast below admin.**
|
|
The live stream maps each event kind to a feature. A kind with no mapping — a new event from a shard
|
|
plugin the site doesn't know yet, say — goes to admins only. It fails closed. This is what keeps the
|
|
stream safe by default when the shard starts sending something new: the worst case is that staff see
|
|
it and players don't, never the reverse.
|
|
|
|
## 5. How it's enforced
|
|
|
|
Three places, one config:
|
|
|
|
- **Page and API requests** are checked before the handler runs, and the response is then stripped of
|
|
any field above the caller's rung.
|
|
- **The live stream** resolves a viewer's rung once, when they connect, and freezes it for that
|
|
connection — a long-open page can't gain privilege because something changed underneath it. Each
|
|
event is then gated and stripped per viewer, so two people watching the same page can legitimately
|
|
receive different versions of the same event, or one of them nothing.
|
|
- **Navigation** hides links a viewer can't follow, so they don't hit a wall. This is presentation
|
|
only — the gate is server-side either way.
|
|
|
|
**Stored history answers the same way the live stream does.** The activity feed reads from the event
|
|
log rather than the live stream, but it resolves the *same* question against the *same* config: which
|
|
kinds you may read, and which fields survive. So moving a feature up a rung hides it from the history
|
|
as well as the stream — there is no back door where yesterday's copy of an event is more revealing
|
|
than today's.
|
|
|
|
One deliberate asymmetry: turning **live updates** off for a feature stops the push, not the reading.
|
|
The marketplace ships this way — its history and its pages are public, only the firehose is off.
|
|
|
|
Changes take effect within about five seconds, **including on streams that are already open**. You
|
|
don't need to restart anything.
|
|
|
|
If the database is briefly unreachable, the site falls back to the built-in defaults — the pre-v3
|
|
behavior — rather than to "everything is public".
|
|
|
|
## 6. Worked examples
|
|
|
|
**"I want a private shard — nothing public until people register."**
|
|
Set every feature to **Signed in**. Anonymous visitors still get the site itself; the shard data
|
|
disappears from the nav.
|
|
|
|
**"Publish the market, but don't tie vendors to players."**
|
|
Marketplace → Everyone, with **Vendor owner name** → Staff. Prices, items and locations stay public;
|
|
who owns each vendor doesn't.
|
|
|
|
**"Leaderboards for members only."**
|
|
Leaderboards → **Linked players**. Anyone who's linked a game account sees the standings; drive-by
|
|
visitors don't.
|
|
|
|
**"Let players see house owners."**
|
|
Houses → Everyone, **House owner** → Linked players. Note this is a real disclosure: house ownership
|
|
is visible in game, but the website makes it searchable in a way the game doesn't.
|