Overview
Live state of this server.
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.
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
/v1. Trailing slashes are normalised./v1/models from the server above.
Enter a model name manually
/v1/models. The name
must match exactly, including any :tag.
Generation & auth
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.
/v1. Any OpenAI-compatible speech endpoint.API key
Browser playback
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.
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
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.
- 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