Rotate administrator keys with safe re-enable and exact retry
This commit is contained in:
1 parent
9cf8249398
commit
55b47ce039
20 files changed
+3072
-104
No files matched your search
+65
-17
@@ -6,26 +6,54 @@ Open **Administration → Accounts** as an enabled owner. Operators, viewers and
|
||||
|
||||
Choose a name, owner/operator/viewer role and operational reason, then review the exact account ID, name, role and reason before confirming. The browser generates a new UUID and 256-bit personal access key with Web Crypto. It saves the exact request in this tab before sending it. The server stores only its SHA-256 hash and returns account metadata, **not the key**. After confirmation, save the one-time displayed key privately and explicitly clear its tab record. Refresh restores an unconfirmed request or the confirmed key until it is cleared. If the response is uncertain, retry the same saved request; do not generate a replacement request or discard the only key copy. Do not place keys in reasons, screenshots, issue reports or shared logs.
|
||||
|
||||
## Change or disable access
|
||||
## Change roles, disable access or rotate a key
|
||||
|
||||
Choose an enabled account other than your own. Review its exact name, ID,
|
||||
current role, requested new role or disable, and operational reason before
|
||||
sending. A role change uses the current account timestamp; a stale target
|
||||
is rejected and must be reviewed again. Disabling blocks the key and
|
||||
permanently invalidates current sessions; this page cannot re-enable the
|
||||
account. **Do not manually re-enable its database row:** without a separate
|
||||
key rotation, its old access key would become usable again. Provision a new
|
||||
account instead. Another owner must perform a self-demotion or self-disable.
|
||||
The database refuses to remove the final enabled owner. Existing game rooms,
|
||||
seats and recovery kits are not modified.
|
||||
Choose another account and review its exact name, ID, current role and
|
||||
enabled state, target action, account timestamp and operational reason.
|
||||
Role changes and disabling require an enabled target. A stale target is
|
||||
rejected and must be reviewed again. Disabling blocks the existing access
|
||||
key and permanently invalidates current sessions. Another owner must
|
||||
perform a self-demotion or self-disable. The database refuses to remove the
|
||||
final enabled owner. Existing game rooms, seats and recovery kits are not
|
||||
modified.
|
||||
|
||||
Each operation has an exact saved identifier and request fingerprint. Concurrent or repeated identical requests resolve to one account change and one actor-attributed audit record; a different request cannot reuse that identifier. Live owner role, session expiry/generation and target version are rechecked at the database write, not inferred from the rendered page. An uncertain role or disable response can be retried from this tab; a conflicting or revoked request remains blocked for review. Tab records are scoped to the original owner account.
|
||||
**Rotate key** works for another enabled or disabled account. The browser
|
||||
generates a fresh target-bound 256-bit key, saves the exact request in this
|
||||
tab, and asks for confirmation of the target and session impact. Rotation
|
||||
replaces the old hash, increments the session generation, revokes existing
|
||||
sessions and enables the account while retaining its name and role. A
|
||||
disabled account is re-enabled **only with the new key**. Previously
|
||||
retired key hashes are kept privately so later A→B→A rotations cannot
|
||||
revive an old credential. Never use manual SQL to re-enable an account
|
||||
without rotating its key. Rotation of your own account is unavailable.
|
||||
On success, save the one-time displayed key privately before clearing its
|
||||
tab record. The server never returns the key. An uncertain response must
|
||||
be retried with the same saved request; discarding it may lose the only
|
||||
copy. A retry confirms a historical operation, not that its key still
|
||||
works: another owner may have rotated or disabled the account afterward.
|
||||
The page compares the latest visible directory row with the saved
|
||||
operation and warns when it changed or is not on the current page.
|
||||
Even matching metadata is only a last-read observation; verify sign-in
|
||||
before distributing a key.
|
||||
|
||||
Action history shows successful account operations with safe role changes and an operational reason for authorized readers. External CLI provisioning or revocation may still have no human actor attribution. Rejected attempts are not yet part of that history. No browser account operation grants access to a playing seat or reveals saved game data.
|
||||
Each operation has an exact saved identifier and request fingerprint.
|
||||
Concurrent or repeated identical requests resolve to one account change
|
||||
and one actor-attributed audit record; a different request cannot reuse
|
||||
that identifier. Live owner role, session expiry/generation and target
|
||||
version are rechecked at the database write, not inferred from the
|
||||
rendered page. An uncertain response can be retried from this tab; a
|
||||
conflicting or revoked request remains blocked for review. Tab records
|
||||
are scoped to the original owner account.
|
||||
|
||||
Action history shows successful account operations with safe role/enabled
|
||||
changes and an operational reason for authorized readers. External CLI
|
||||
provisioning or revocation may still have no human actor attribution.
|
||||
Rejected attempts are not yet part of that history. No browser account
|
||||
operation grants access to a playing seat or reveals saved game data.
|
||||
|
||||
## Verification and remaining work
|
||||
|
||||
Implementation: [account query and transactions](../db/admin-accounts.ts), [request contract](../lib/admin-accounts.ts), [route](../app/api/admin/accounts/route.ts), [page](../app/admin/accounts/page.tsx) and [additive operation receipt](../drizzle/0019_admin_accounts.sql). Keep the original owner-protection and session-revocation triggers in migration `0008`.
|
||||
Implementation: [account query and transactions](../db/admin-accounts.ts), [request contract](../lib/admin-accounts.ts), [route](../app/api/admin/accounts/route.ts), [page](../app/admin/accounts/page.tsx), [original receipt](../drizzle/0019_admin_accounts.sql) and [additive rotation receipt/key history](../drizzle/0020_admin_key_rotation.sql). Keep the original owner-protection and session-revocation triggers in migration `0008`.
|
||||
|
||||
```sh
|
||||
npm test -- admin-accounts admin-audit
|
||||
@@ -42,9 +70,29 @@ confirmation. Independent security and persistence reviews resolved
|
||||
unchanged-role audit no-ops, missing point-of-risk copy and a logout-all
|
||||
timestamp rollback.
|
||||
|
||||
The rotation follow-up passes typecheck, lint and 5,787/5,787 offline
|
||||
cases, the app build, Drizzle migration check and 55/55 HTTP tests against
|
||||
the supported local development server. An isolated built-worker D1 store
|
||||
with its own disposable owner/target completed authenticated **network**
|
||||
provision, rotation, exact replay, disable, fresh-key re-enable, old
|
||||
key/session rejection, A→B→A refusal, safe audit and foreign-origin
|
||||
denial. No production account, room or credential was changed.
|
||||
An actual phone-width owner page exposed the enabled-target review and
|
||||
confirmation with no key before submission; a separate local-only
|
||||
completed-record fixture showed the superseded-key warning without
|
||||
sending a mutation. Independent security review found the historical
|
||||
replay presentation risk, now corrected by last-read directory
|
||||
comparison; persistence review reported no scoped finding.
|
||||
|
||||
The full 55-case suite against `wrangler dev` of the production build did
|
||||
**not** pass: the local workerd process intermittently returned 503
|
||||
mid-request under that suite. A single oversized request returned 413
|
||||
and later requests succeeded after draining its body, but that does not
|
||||
resolve the broader worker-restart observation. Development-server
|
||||
success and focused built-worker mutations are not deployed acceptance.
|
||||
|
||||
Use an isolated owner and disposable target for authenticated end-to-end
|
||||
account changes; never use a person's normal owner key in a destructive QA
|
||||
flow. Client/browser fixtures alone do not prove a signed-in **network**
|
||||
HTTP mutation or production deployment. Key rotation, re-enabling a
|
||||
disabled account, failed-attempt audit, complete administration and live
|
||||
acceptance remain unfinished.
|
||||
HTTP mutation or production deployment. Failed-attempt audit, complete
|
||||
administration and live acceptance remain unfinished.
|
||||
@@ -18,12 +18,14 @@ 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 and disabling identify the acting owner and record
|
||||
the supplied reason. 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, and later
|
||||
operations may supersede it. It does not assert the room's current state.
|
||||
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
|
||||
|
||||
@@ -55,8 +57,10 @@ Implementation: [query/projection](../db/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); it does not add a
|
||||
new raw game or message source.
|
||||
[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
|
||||
@@ -76,7 +80,7 @@ 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,
|
||||
key rotation and re-enable, 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.
|
||||
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.
|
||||
+9
-7
@@ -9,7 +9,7 @@ recovery and persistence systems where suitable.
|
||||
|
||||
| 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](ADMIN_ACCOUNTS.md) adds a paginated directory, browser-created one-time keys, role changes, disabling, live authority and exact retries. A room host is not a site administrator. Key rotation/re-enable, failed-attempt history and further operational permissions remain. | Prototyped, partial |
|
||||
| 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](ADMIN_ACCOUNTS.md) 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 and 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](ADMIN_ROOM_CREATION.md) 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](ADMIN_LOBBY_CONFIGURATION.md) 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](ADMIN_ROOM_CONTROLS.md), [administrative close/reopen](ADMIN_ROOM_CLOSURE.md) and [recoverable removal/restoration](ADMIN_ROOM_REMOVAL.md) have controls, durable audit, exact retries and player/AI enforcement. [Archive/unarchive](ADMIN_ROOM_ARCHIVE.md) has separate directory filters and exact retries. Permanent deletion and bulk actions remain missing. | Prototyped, partial |
|
||||
@@ -53,12 +53,14 @@ Disabling an account invalidates its sessions; later re-enabling it cannot resto
|
||||
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](ADMIN_ACCOUNTS.md) lets a current owner provision a personal
|
||||
key, change another administrator’s role or disable its access. The exact
|
||||
request and generated key remain tab-scoped through uncertain responses;
|
||||
the server never returns a key. The owner reviews the target and reason
|
||||
before submitting, and no game or seat is modified. Key rotation and
|
||||
re-enabling disabled accounts are not available in this prototype.
|
||||
[Manage accounts](ADMIN_ACCOUNTS.md) 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](ADMIN_PARTICIPANT_AI.md) lets owners/operators support an absent
|
||||
human in a paused, started game. The player retains access and takeback controls;
|
||||
|
||||
+14
-9
@@ -1,6 +1,6 @@
|
||||
# Current development status
|
||||
|
||||
Updated 29 September 2026. **The full twelve-faction goal is unfinished. Basic
|
||||
Updated 30 September 2026. **The full twelve-faction goal is unfinished. Basic
|
||||
is playable and the six classic factions have a user-authorized Advanced preview.
|
||||
Expansion starts and full rules/publication acceptance remain gated.**
|
||||
|
||||
@@ -34,14 +34,19 @@ arrival composition, natural assassination reveal coverage, normal
|
||||
expansion starts or deployed acceptance.
|
||||
|
||||
The owner-only [administrator accounts](ADMIN_ACCOUNTS.md) prototype now
|
||||
lists accounts, provisions a browser-generated one-time personal key, changes
|
||||
another account’s role and disables access. Server actions repeat live owner
|
||||
authority and target-version checks at commit, preserve exact tab-scoped
|
||||
retries and attribute successful changes in action history without exposing
|
||||
keys or saved games. Key rotation/re-enable, rejected-attempt history,
|
||||
authenticated positive **network** HTTP on a dedicated isolated QA account, deployed
|
||||
acceptance and the rest of full administration remain unfinished. This code
|
||||
is not in the last verified live image.
|
||||
lists, provisions, changes roles, disables and rotates another account’s
|
||||
personal key. Rotation can re-enable a disabled target only with a fresh
|
||||
key; old credentials and sessions remain revoked, including attempted
|
||||
A→B→A reuse. Live owner/target fences, durable exact retries and safe
|
||||
actor-attributed history preserve rooms and seats. A disposable owner
|
||||
and target completed authenticated local **network** rotation and
|
||||
re-enable on an isolated built-worker D1 store, and mobile browser review
|
||||
showed exact impact and a superseded-replay warning. The supported dev
|
||||
HTTP suite passes 55/55; the built-worker full suite hit intermittent
|
||||
local workerd 503 restarts and is not a green production-bundle suite.
|
||||
Failed-attempt history, the rest of administration, deployment and
|
||||
production acceptance remain unfinished. This code is not in the last
|
||||
verified live image.
|
||||
|
||||
The bounded [Advanced Moritani Assassinate Leaders preview](MORITANI_ASSASSINATE_LEADERS.md)
|
||||
now has repeatable genuine games across two through six supported seats.
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
# Dune implementation status
|
||||
|
||||
## 30 September 2026 — owner key rotation and safe re-enable
|
||||
|
||||
The [account panel](ADMIN_ACCOUNTS.md) now rotates another enabled or
|
||||
disabled administrator’s personal access key with one exact tab-scoped
|
||||
request. A disabled target becomes enabled only with the new key; all
|
||||
old sessions are revoked and historical hashes prevent A→B→A key reuse.
|
||||
The owner reviews name, ID, role, enabled state, reason and revocation
|
||||
impact; one-time key disclosure remains confined to the confirmed tab.
|
||||
Additive migration `0020` preserves previous account receipts and games.
|
||||
Live SQL owner/version/key checks, one durable rotation receipt and
|
||||
actor-attributed safe audit prevent stale or duplicate changes.
|
||||
|
||||
Independent persistence review found no scoped issue. Security review
|
||||
identified an exact historical replay that could show a superseded key
|
||||
as currently usable. The client now compares its receipt with the
|
||||
last-read account metadata, warns when changed/unlisted, and advises
|
||||
sign-in verification even when matching. The focused security follow-up
|
||||
found no remaining issue in those changed lines. A phone-width
|
||||
authenticated local page showed exact pre-send confirmation; a
|
||||
completed-record fixture showed the superseded warning without sending
|
||||
a mutation.
|
||||
The isolated built-worker network smoke provisioned, rotated, replayed,
|
||||
disabled and re-enabled a disposable target, rejected old keys and
|
||||
sessions, refused key reuse and preserved safe audit and origin denial.
|
||||
No live account or saved game was modified.
|
||||
|
||||
`npm run check` passes typecheck, lint and 5,787/5,787 offline cases;
|
||||
`npm run build`, `drizzle-kit check` and all 55/55 development-server
|
||||
HTTP cases pass. A second full HTTP run against the local production
|
||||
bundle did not pass: workerd intermittently returned 503 mid-request
|
||||
under the suite. Draining oversized admin bodies removed an early
|
||||
unread-stream return; isolated built requests returned 413 and later
|
||||
requests remained healthy, but the full bundle load issue remains
|
||||
unresolved. None of this proves a deployed revision, complete
|
||||
administration or production recovery.
|
||||
|
||||
## 29 September 2026 — natural Advanced Moritani reveal and replacement
|
||||
|
||||
A source-unchanged genuine six-seat sample at base seed `20261307`
|
||||
|
||||
Reference in new issue
Block a user