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.
Stored, not yet applied. These dials are persisted and served, but no engine code reads them yet — wiring them is the balance pass. They are shown because the surface needs testing; they are labelled because a control that looks live and does nothing is worse than no control.

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

Not implemented — by design, for now

User management

There is no user database. Access is a single shared passphrase (EA_PASSPHRASE), and a display name is whatever a player types.

So there is nothing to administer: no per-person record to edit, no credential to reset, no role to grant. A user-management screen over this model would be a UI that lies. Live presence is under Players, which is the honest version of this view.

What real accounts require

  • Durable identity store — the same storage gap that blocks seasons
  • Per-account credentials and a reset path
  • Roles: operator versus player, so this panel can be exposed safely
  • Character ownership bound to an account rather than a browser
  • Ban and suspension records, which must outlive a session

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