Files
Dune/docs/ADMIN_ACTION_HISTORY.md

87 lines
4.9 KiB
Markdown

# Administrative action history
Open **Administration → Action history** to review recorded operations. The
read-only page unifies the nine existing durable receipt sources: administrator
access, pause/join controls, room creation, lobby configuration, removal,
close/reopen, archive, participant AI and discussion moderation. It does not
create another audit store or change saved games.
Search room/seat/account identifiers or current acting/affected administrator
names. Filter by action group and inclusive UTC calendar dates, then browse
25 records per page. Events sort newest first with deterministic source/receipt
ordering for matching timestamps. New operations can move records between pages;
refresh to fetch current history. Filters and page number live in the URL and
survive refresh and browser navigation. No credentials or reasons enter that URL.
Each record identifies the action, time, room/seat/account where recorded, actor,
operational reason when permitted, and bounded before/after settings. Expand
**Recorded settings** for the public configuration, lifecycle flags, revisions,
AI difficulty or changed player circle. Names come from current administrator
accounts; deleted accounts retain their recorded identifiers. Web-managed
provisioning, role changes, disabling and fresh-key rotation/re-enable
identify the acting owner and record the supplied reason. Rotation shows
only the before/after enabled state, never the old or new key, hash or
session. External CLI provisioning/revocation may lack human actor
attribution, and the page says so. A **Recorded** outcome means the
operation was applied; a room-control operation may successfully save
identical settings; later operations may supersede it. It does not assert
the room's current state.
## Access and privacy
All administrator roles may read the safe event envelope and public operational
settings. Only a live owner/operator receives reasons; viewers receive null.
Reasons are excluded from search for every role, preventing a hidden-reason
match/count oracle. Search uses the same validated identifiers and names that
can be displayed, rather than raw historical JSON. Unknown actions and malformed
metadata retain a generic record with unavailable details.
Count and page reads repeat live session, enabled-account, generation, expiry and
role checks inside one database transaction. Reason visibility derives from that
live role, not the role captured during earlier authentication. The browser binds
its read to the signed-in account, clears old results on refresh/error, and
revalidates when focus/visibility or page restoration returns. Already disclosed
information cannot be withdrawn from a browser or copied records.
The query reads durable receipts directly and joins only current administrator
names. It does not join saved games, messages or player credentials. Hashes,
access/session/recovery secrets, session IDs, game state, cards and private
messages are excluded. Raw metadata never reaches the response. Ordinary room
hosts and unauthenticated callers receive no audit history. The route is GET-only
and uses the existing no-store, cookie-varying administration boundary.
## Evidence and remaining scope
Implementation: [query/projection](../db/admin-audit.ts),
[response contract](../lib/admin-audit.ts),
[route](../app/api/admin/audit/route.ts), [page](../app/admin/audit/page.tsx).
The original nine-source read needed no migration. Owner account management
adds a safe `reason` field and durable retry receipts in additive
[migration 0019](../drizzle/0019_admin_accounts.sql). Additive
[migration 0020](../drizzle/0020_admin_key_rotation.sql) keeps rotation
receipts and retired key hashes out of the history projection. Neither
adds a raw game or message source.
```sh
npm test -- admin-audit
npm run test:integration -- admin-boundary
```
Focused checks cover nine sources, shared operation IDs, deleted targets,
malformed/secret-shaped metadata, reason visibility and search exclusion,
commit-time demotion/revocation/expiry, UTC dates, deterministic pagination and
strict client allowlists. A real room-creation/configuration transaction verifies
the one-based player-circle display. The dedicated operator HTTP verifier reads
its own newly created moderation receipts and checks account binding.
Independent review corrected malformed role coercion, rejected-field search
(including embedded NULs), affected-account name lookup and position-only
configuration evidence. An isolated workerd/D1 regression exercises all nine
sources under its five-term compound-query limit, with live role demotion and
filtered reads. Materialized groups keep the unified query within that limit.
Existing records cover successful operations only. Failed/rejected attempts,
full operational health/diagnostics, backup/import/restore and the
remaining participant support are still required. Local/browser/source-bound
evidence is separate from deployed acceptance, which must identify the
actual running revision and use authorized QA access.