Files
Dune/docs/ADMIN_PANEL.md
T
Quintine e493bcf58e Report database size and make four genuine suites reproducible
Operations now report the SQLite database size and page count, read through
PRAGMA page_count/page_size in the same authority-checked batch as the
lifecycle counters with non-numeric values clamped to zero; the page renders
both and states that free disk space is still unknown.

replaceMoritaniAssassinationTraitors draws the first distinct physical Traitor
Card from the reserve instead of the reserve's first entry, because the face-up
set-aside card can still sit in that list: re-drawing it produced a receipt
whose replacement equalled the revealed card, which the assassination integrity
check rejects mid-Mentat. With no distinct card left the step is blocked with an
explicit reason instead of committing an invalid receipt.

Four tests were made reproducible after measuring their real failure rates,
because a suite that fails for random reasons cannot act as a checkpoint gate:
the Discovery typed Spice Blow test now removes other cards for the same
territory so one blow deposits the printed amount (0/20 after, ~20% before); the
Discovery E2 skills deal is pinned with the new shared
tests/deterministic-random.ts helper so a Lasgun-shield explosion cannot
pre-empt its skill consumer (0/12 after, ~20% before); the Ecaz Occupy Ix/CHOAM
policy check accepts any legal continuation and still asserts the phase it
reaches (0/30 after, ~10% before); and the mixed-E3 Moritani Cunning test keeps
the random storm off red_chasm's single sector (0/25 after, ~20% before).

Offline suite 7,801/7,801 with typecheck and lint clean. No game, seat, saved
state or deployment changes.
2026-10-07 19:55:43 +11:00

19 KiB
Raw Blame History

Administration panel requirements

Added to the goal on 22 September 2026 at the user's request. This is required product scope. The first access and directory prototype is available at /admin; the remaining room operations are unfinished. Reuse the existing room, session, recovery and persistence systems where suitable.

Required capabilities

Area Required behavior Stage
Administrator access Personal access keys, server-enforced owner/operator/viewer roles, persistent eight-hour sessions and individual/all-session sign-out. Owner-only account management adds a paginated directory, browser-created one-time keys, role changes, disabling and fresh-key rotation/re-enable with session revocation, live authority and exact retries. A room host is not a site administrator. Failed-attempt history records rejected sign-ins and role denials without storing any credential; further operational permissions remain. Prototyped, partial
Room directory Search room codes/public player names, filter status/rules/availability, sort and paginate; show host, roster, modules, game change time, pause/join flags and bounded decision ownership. Detailed setup/shared-window ownership remains. Prototyped
Create and configure Create a lobby with an explicitly owned new host seat, Basic/Advanced preview and initial AI configuration; invite humans and use ordinary lobby controls/voluntary host-seat handover. Neutral lobby configuration adds rules/modules, AI seats and host assignment to an existing human without private access. Reserved human seats remain. Prototyped, partial
Lifecycle and removal Pause/resume and joining locks, administrative close/reopen and recoverable removal/restoration have controls, durable audit, exact retries and player/AI enforcement. Archive/unarchive has separate directory filters and exact retries. Permanent deletion and bulk actions remain missing. Prototyped, partial
Participant support Participant AI enables an existing difficulty on an eligible human seat in a paused game, retaining access/takeback and private custody. Discussion controls mute/unmute new sends while retaining history and gameplay. Participant removal, access revocation, assisted recovery and further replacement remain. Existing lobby controls can reassign the host. Prototyped, partial
Saved-game operations Owner-only room backups capture/list/download a size-limited, version-fenced room snapshot without changing play or access; import validation, safe checkpoint restore and interrupted-work diagnosis/resume remain missing. Prototyped, partial
Operations and audit Action history searches existing durable operations with safe settings and role-limited reasons. The owner-only operations page samples build revision, room/seat flags, JSON syntax failures and backup/export counts without exposing game contents. Stalled-decision diagnosis adds a bounded, read-only sample of live rooms whose public pending interaction has been idle longest, with aggregate counters. An explicit owner action runs read-only SQLite quick/foreign-key checks and returns only pass/fail, never corrupt row details. Failed-attempt history records rejected authentication and authorization attempts with a fixed reason, the attempted role and an already-known account id — never a key, token, body or address — and exposes seven-day counters plus a bounded recent sample to the owner. The aggregate sample also reports the SQLite database size and page count; free disk space and maintenance controls remain. Prototyped, partial

Track each area through Missing, Prototyped, Integrated, Verified and Polished. As implementation begins, link its controls, server actions, persistence, documentation and evidence. A working directory alone is not a full admin panel.

Access and directory prototype

Use Administration in the home-page footer or visit /admin. Every directory request checks a separate administrator session; seat cookies and forwarded identity headers cannot grant access. The standalone NAS runtime has no trusted Sites identity dispatcher, so this panel uses separately provisioned personal keys. Each key contains a random 256-bit secret. Only its SHA-256 hash is stored; sign-in issues a separate random, hashed session with an eight-hour absolute expiry. HTTPS uses Secure, HttpOnly, SameSite=Strict cookies scoped to the admin API. Mutations require the exact configured origin and bounded JSON requests.

All three roles may read the current directory and sign out their own sessions. Owners/operators may pause/resume rooms and lock/unlock new joins through Room controls. The role is checked from the live account, never accepted from a client. Owners/operators can also create a configured lobby, with explicit new-host ownership and saved exact retries. Configure lobby changes supported rules, AI seats and the host without granting private seat access. Remove or restore blocks ordinary room access while preserving the game, seats and access revocations. Use the Directory filter to find removed rooms; restoration retains prior pause and joining settings. Close or reopen ends further play without awarding a winner and preserves readable private tables and discussion. Reopening retains the saved game, pause/join settings and current credentials. Archive or unarchive organizes closed rooms with separate directory filters and exact retries. Unarchiving leaves play closed; restoring a removed archive preserves both flags. Disabling an account invalidates its sessions; later re-enabling it cannot restore them. The database refuses to disable, demote or delete the final enabled owner. No default key, public account creation or host-to-administrator promotion exists.

Manage accounts lets a current owner provision a personal key, change another administrator’s role, disable access or rotate another account’s key. Rotation also re-enables a disabled account with a fresh key while invalidating every earlier key and session. The exact request and generated key remain tab-scoped through uncertain responses; the server never returns a key. The owner reviews the target, current state, reason and revocation impact before submitting. No game or seat is modified.

Participant AI lets owners/operators support an absent human in a paused, started game. The player retains access and takeback controls; the administrator receives no private seat. Resume remains a separate action. Discussion controls restrict new messages from a human seat, including after recovery/handover, without exposing private history.

Private room backups let an owner capture the current saved state and related room-owned records without pausing or editing the room. The metadata directory omits private contents; downloaded JSON contains secret game state and hashed seat/recovery credentials. Import/restore are not available.

The owner-only operations page provides manual read-only samples of room lifecycle counts, active seat credentials, invalid JSON saves, latest room write, backup payload usage, export receipts, and the SQLite database size and page count. The database query repeats current owner authority. Run integrity check is a separate, explicit read-only D1 quick check and foreign-key check. It repeats owner authority inside the query batch and returns only pass/fail and sample time, never a table name, row ID, private save or SQLite diagnostic text. The check can take time; a failed check calls for a protected snapshot and private diagnosis, not automatic repair. It does not prove disk capacity, backup recoverability or valid game rules.

Failed-attempt history adds List failed attempts: seven-day per-reason counters and the twenty most recent rejected administrator requests — malformed, unknown or disabled keys, missing, unknown, expired or revoked sessions, and role-denied actions. Recording is best-effort inside the rejection path and stores no credential; the read repeats live owner authority and answers no-store. The history is diagnostic: it never locks out, disables or revokes anything by itself.

Stalled-decision diagnosis adds List stalled decisions: a bounded, read-only sample of at most ten live rooms whose public pending interaction — a decision, response, Truthtrance or phase opening — has been idle longest, with the room code, idle duration, interaction kind, turn and phase. Paused, closed, removed and archived rooms are excluded, no private game content is selected, and the sample repairs nothing; the aggregate sample also reports the stalled count and the oldest stalled write. A stall is inferred from the room write time, so a slow human turn counts too.

The directory uses an explicit field allowlist, independent of any player view. It never returns hands, Traitors, predictions, plans, private messages, recovery records or raw saved state. Searches inspect only codes and public player names. Each page is limited to 25 rooms and repeats authorization in its read transaction so concurrent revocation cannot expose a later directory snapshot. Malformed saves are marked unreadable without leaking their contents or breaking searches. Last game change refers to the room's saved-state timestamp, not private messages. Shared windows and setup currently use broad pending labels; these labels do not assert that a human is stalled or identify hidden-card-dependent eligibility.

Implementation: panel, access, directory, HTTP boundary, additive migration, and operator CLI. The migration's explicit SQLite triggers enforce final-owner protection and account revocation/provisioning audit; retain them when generating future schema changes. Action history unifies the existing successful-operation records; broader audit coverage remains.

Initial owner and access recovery

The NAS container provisions an Initial owner automatically after migrations only if admin_accounts has no rows. Its newly generated personal access key is printed once in that container's startup log (docker logs <container-name>). Retrieve it privately, save it in a password manager and sign in at /admin; it is not a chosen password or an environment variable. Anyone able to read that log can use the key. Restrict log access and retention; do not paste the log into support reports. Restarts, container replacements and existing accounts (even disabled ones) never generate or print another key. Save the key before replacing the first container, since its original log may disappear. Losing it requires manual owner recovery below.

The automatic bootstrap applies only to the bundled NAS container startup, not npm run dev or an external D1 deployment.

For manual provisioning or access recovery from the development checkout, apply additive migrations, then create a personal owner key in a new private directory outside the checkout:

npm run db:local
node tools/admin-access.mjs --name 'Owner' --role owner --out /private/dune-owner
npx wrangler d1 execute DB --local --config tools/wrangler.local.json --persist-to .wrangler/state --file /private/dune-owner/provision.sql

The parent directory must already exist. Output files are mode 0600 in a new mode-0700 directory. access-key.txt contains the sign-in key; provision.sql contains only its hash. Keep the key in a private password manager or protected file, outside Git, logs and chat. The command never connects to a database or prints the key. Apply the SQL only to the intended instance. Do not upload a development key to production; provision separate accounts there.

For the container, run the bundled CLI inside the existing dune container (using its actual container ID/name), with output such as /data/admin-owner. Apply the generated SQL using node node_modules/wrangler/bin/wrangler.js d1 execute DB --local --config tools/wrangler.local.json --persist-to /data --file /data/admin-owner/provision.sql. A protected volume snapshot should precede operational changes. Retrieve the key through an authorized private channel and retain the account ID shown by the CLI. No application restart is needed.

To revoke an account, generate a new private output directory with node tools/admin-access.mjs --revoke ACCOUNT_UUID --out /private/dune-revoke and apply its revoke.sql using the same instance-specific command. To replace a lost/compromised owner key, provision a replacement owner first, verify access, then revoke the previous account. Existing games and seats are unchanged. Sign out everywhere revokes browser sessions but keeps the personal key valid.

Focused verification

npm test -- admin-access admin-directory admin-http
npm run test:integration -- admin-boundary
node tools/verify-admin.mjs --url http://localhost:3000 --key-file /private/dune-qa/access-key.txt --qa-account ACCOUNT_UUID --out /private/dune-admin-report

The HTTP verifier requires a dedicated QA administrator account and explicitly checks its ID before proceeding: it creates a named QA room and signs out all sessions of that QA account. Never supply a human operator's normal key. It checks authentication, cookie properties, ordinary-host denial, directory privacy, session revocation and unchanged game/seat continuation. Operator/owner QA keys also create dedicated rooms for joining locks, pause/takeback/resume, administrator creation, invitations, voluntary host handover and neutral lobby configuration with existing-human host assignment and a real game start. They also close/reopen, archive/unarchive and remove/restore separate QA games, preserving their paused state, discussion, saved seats and revoked credentials. Private reports retain room IDs and safe outcomes, never keys/cookies. The container verifier provisions its own disposable QA account and checks admin-session continuity across restart and replacement, followed by the HTTP flow and HTTPS-proxy cookie behavior. Participant checks use another new QA room for paused AI enable, private-seat continuity, takeback and durable retries after resuming. Discussion checks use a new QA room for mute/unmute, own-only status, incoming messages and saved retries.

Local browser checks cover sign-in, directory filters, refresh restoration and mobile layout. Final source-bound checks and separately recorded deployed acceptance accompany each checkpoint; a local pass is not production evidence.

Behavior and safeguards

Every privileged endpoint must authenticate and authorize its caller. Support least-privilege roles, secure initial provisioning, safe credential/session management, and accessible desktop/mobile controls. Do not ship default shared credentials or expose administrator capabilities through ordinary room cookies.

Default views must preserve hands, Traitors, predictions, sealed plans, private discussion and recovery credentials. Any necessary private support access must be separately authorized and audited; it must not reveal information to other players or feed an administrator's playing seat or AI strategy. Redact secrets and private card contents from routine logs and reports. Backup access needs equivalent protection, since backups can contain private state.

Pausing must stop human mutations and automatic/AI continuation consistently without losing pending decisions. Resuming restores the same turn and pacing. Seat removal or reassignment must explain the effect on an active game and preserve a legal continuation; do not simply delete a faction's physical pieces. Existing consent/recovery boundaries remain applicable to seat access.

Use authoritative rules and the existing concurrency model. Reject stale or duplicate destructive actions safely. Imports and restoration must validate schema, custody, private-information boundaries and session ownership; capture a recovery checkpoint before replacing current progress. Never restore old credentials in a way that silently reauthorizes a revoked participant.

Room removal should be reversible by default. Permanent deletion and bulk operations must identify the selected rooms and dependent records, explain irreversibility, require deliberate confirmation and retain appropriate audit evidence without keeping purged secrets. Protect unrelated games. The existing saved-game preservation rules still govern development and maintenance; they do not preclude intentional room deletion through the authorized product workflow.

Record who acted, when, the affected room/seat, operation, reason, outcome and safe before/after metadata. Maintenance controls must warn about interruptions to active games and preserve restoration. Do not recreate recurring restarts or cleanup schedules. Do not add an arbitrary game-state editor that bypasses rules, silently changes outcomes or makes unfinished modes appear complete.

Development order and acceptance

Prototype permissions and the room directory first, then room creation and reversible lifecycle operations, participant support, backup/restore and operational tools. Include functional server actions and saved continuation in each slice; continue independent game features in parallel where useful. Administration is part of the non-AI completion gate in the AI development plan.

Acceptance requires working controls and documented setup/recovery procedures, with authenticated HTTP and persistence tests for authorization, privacy, concurrent actions, exact retries, related-record handling and safe restoration. Verify pause/resume with both humans and AI, archive/delete/restore with saved rooms, and denied admin actions by ordinary players. Exercise desktop/mobile flows and server restart recovery. Use dedicated test rooms and preserve all unrelated saved games; complete independent security/privacy/persistence review. Verify the panel's relevant deployed flows at https://dune.procrastination.games, including denied access by ordinary players, using the production verification workflow. The full goal remains unfinished until this scope and the other completion criteria are satisfied.