Administratum
← the Hall

Overview

Live state of this server.

Narrator
Buffers

Lobbies

Every live engagement, with its occupants.

Players

Live connections. Identity is a self-declared display name — there are no accounts yet, so this is presence, not a user list.

Campaigns

The loaded campaign, as the engine sees it.

Placeholder

Campaign builder

Not built. Campaigns are authored as packs/<id>/campaign.json and validated at boot — a malformed campaign crashes startup rather than surfacing as a broken room.

A builder must write pack files, and this container runs with a read-only filesystem, so authoring needs a writable content volume first. Until then editing the JSON directly is the supported path and this tab would only be a worse text editor.

  • Scene sequencing across acts, with kind (battle / story / rest)
  • Location graph and route validation
  • Threat budget preview per scene
  • Prologue slide ordering and art binding

Content

Portrait coverage. Served from ART_DIR, outside the web root, so art can be added to a read-only container through a mounted volume.

Config

Any OpenAI-compatible server. One code path covers Ollama and LM Studio. Full guide: docs/LLM-CONFIG.md

Provider
Must end in /v1. Trailing slashes are normalised.
Press Load to read /v1/models from the server above.
Enter a model name manually
Use when the server does not implement /v1/models. The name must match exactly, including any :tag.
Off by default. Deterministic narration always ships first; LLM prose arrives after, or not at all — a dead model costs a turn nothing.
Generation & auth
Never echoed back — only whether one is set. Blank leaves it unchanged.

Optional text-to-speech server. Narration and the prologue are read aloud by a local synthesiser — nothing is sent to the wider network, and the voice never touches a single roll.

Provider
Must end in /v1. Any OpenAI-compatible speech endpoint.
Press Load to read the list from the server.
Slightly under 1 reads better for narration.
mp3 is the safest across browsers.
Synthesis is slower than text — cold model load costs seconds.
Off by default. Falls back to silent text when unreachable or disabled.
API key
Browser playback
The prologue is always read aloud when configured. This controls the narration band at the table, where turns can resolve faster than speech. Every player has their own copy of this same switch, right on the narration band during play (the "VOX" button) — this one only affects THIS browser, which is normally the operator's own.
Live. These change the game. All five dials are read by the engine. This box previously said they were inert, which stopped being true once damage and health were wired — a control that IS live while labelled dead is worse than one that does nothing, because you change it, disbelieve it, and stop trusting the panel.

They apply at spawn, not retroactively. Accuracy, damage and health are stamped onto each character as it is created, so a change here affects the next engagement — a fight already under way keeps the numbers it started with, and a saved campaign stays reproducible from its own log. Threat budget applies to the next encounter built.

100% is the shipped value for every percentage dial; 0 is shipped for accuracy. Reset to shipped returns all five.

Lifetimes and ceilings. Every value is bounded server-side; out-of-range input is clamped and the applied value is shown back.

Placeholder

Event management

No event system exists yet. Nothing to configure.

An event is a time-boxed rule override — a modified threat budget, a bonus reward, a themed card pool. Every one of those reaches into resolution, so events depend on the balance multipliers being wired first. Building the UI before the mechanism would be configuring nothing.

  • Window (start / end), with an explicit timezone
  • Rule overrides, drawn from the same schema as Balance
  • Scoped card pool additions — never deletions, per the pack rule
  • Participation record, so a completed event survives its window
Placeholder

Season management

No season system exists yet.

A season needs durable per-character progression to reset or carry, and progression currently commits to an in-memory character on campaign completion. Seasons are meaningless without storage that outlives the process.

  • Season window and label
  • What resets and what is retained — marks and scars are earned history
  • Leaderboard snapshot at close
  • Pack version pinning, so a season replays on the rules it was played under

Logs

Administrative acts. Separate buffer from the action log so routine dice traffic cannot evict the record of a room being closed.

Runtime verbosity. Changed live, because raising the level during an incident must not require a restart — a restart destroys every in-memory session, which is the state you were trying to inspect.

trace logs every action. Bounded ring buffer, so it cannot exhaust memory, but it will evict history faster.

Accounts

Tiers are stored on the account record, so a grant survives a restart and does not depend on EA_ADMIN_ALIAS. That variable is break-glass only: it adds an admin, and cannot remove one.

Grant a tier

Look a name up directly, or pick it from the roster below. Counting a tier is still one indexed count rather than a list-then-filter — but asking an admin to type a name from memory meant a silent typo landed on "no such account" instead of on a person.

The server refuses to remove the last admin, so this panel cannot lock everyone out of itself. It will happily demote you if another admin exists — that is a real thing an operator may need to do, and not something to guess at on your behalf.

Registered accounts

Compliance

What this server holds, why, and for how long. Served from the code rather than transcribed, so the panel and the docs cannot drift.

A subject access request returns everything held about one display name.

Erasure is not offered as a button. All personal data here is in memory and is destroyed when the room is reaped or the process restarts, so the honest answer to an erasure request today is to close the room — a delete control implying durable removal would misrepresent what happens.
Known gaps. Stated plainly so they are not mistaken for solved problems.
  • No durable audit sink — the trail is in memory and lost on restart
  • No retention timer — buffers evict by volume, not by age
  • No consent record, because no data leaves the process
  • Admin endpoints share the game's passphrase; they need their own role
  • No transport encryption on the container itself — terminate TLS ahead of it