Skip to content

Feat/tier5 users admin surface - #8

Merged
raymondproguy merged 6 commits into
mainfrom
feat/tier5-users-admin-surface
Sep 17, 2026
Merged

raymondproguy merged 6 commits into
mainfrom
feat/tier5-users-admin-surface

Conversation

@raymondproguy

Copy link
Copy Markdown
Contributor

No description provided.

raymondproguy and others added 6 commits September 16, 2026 21:18
…'s first write

PUT/DELETE /v1/admin/users/{userID}/metadata/{key} have written since
Tier 3, so "the only writes under /v1/admin" and "the first version with
admin endpoints that WRITE" were both wrong. Says what the two write
blocks actually are instead.

Co-Authored-By: Claude Code <noreply@anthropic.com>
cryden records that a login tripped anomaly signals and has no concept
of a human having read one. reviewed_anomalies (014) is that concept,
keyed on the audit event id, with status as a column so dismissing keeps
the evidence and nothing ever deletes. The foreign key on audit_events
is what refuses a review of an event that does not exist — cryden has no
lookup-by-event-id, so the database answers that question.

Co-Authored-By: Claude Code <noreply@anthropic.com>
GET /v1/admin/users searches by exact email through cryden.GetUser, or
pages every account newest-first. GET /v1/admin/users/{userID} reports
one account with its live session count and recent audit history. The
DTOs leave PasswordHash behind and the detail view can report a lockout
but not clear one, so this surface cannot lock anyone out.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Reports second-factor enrolment and removal as the engine's own audit
events against the user total: EventTOTPEnabled/Disabled and
EventWebAuthnRegistered/Removed, all-time and over a window.

It counts events, not users, and every field is named accordingly.
cryden's TOTPStore and WebAuthnCredentialStore are per-user with no
Count or ListAll, so "how many accounts have MFA" is not a question the
engine can answer — and answering it here would mean counting rows in
cryden's own tables. No adoption percentage is derived for the same
reason: enable/disable churn makes a ratio of events to users a number
that looks like coverage and moves for the wrong reasons. Recovery
codes are excluded; they are a fallback for an account that already has
a factor, not a factor of their own.

Co-Authored-By: Claude Code <noreply@anthropic.com>
GET /v1/admin/anomalies merges cryden's two flagged event types
(anomaly_detected, credential_stuffing_detected) newest first, each with
the review recorded against it. PUT /v1/admin/anomalies/{eventID}
records an operator's judgement, keyed on the audit event id.

A review is a row in this repo's table, never a write to cryden's audit
history, and nothing deletes: dismiss is a status, withdrawing a
judgement stores "unreviewed" rather than removing the row, so the
record that somebody looked and who they were survives. Confirming an
event takes no action on any account — there is no machinery here that
acts, which is what keeps this on the right side of the read-only rule.

Co-Authored-By: Claude Code <noreply@anthropic.com>
CURRENT-STATE, NEXT and PROGRESS get the tier's section: the user
surface, the MFA adoption report's events-not-users decision, the
review queue's two settled decisions, and what is owed — migration 014
never applied, anomalyreview.PostgresStore never run against a real
Postgres, no Docker or Postgres reachable here, -race not run.

Also corrects three earlier claims that the settings routes were the
admin surface's first write. They were not: Tier 3's metadata PUT and
DELETE had written since then. The reading those passages justify is
unaffected — what needed arguing was whether a settings save is the
kind of write the rule forbids, not whether the surface was read-only,
which it never was.

openapi goes to 1.6 with the four new paths and their schemas; README
gains the three sections and its admin route list is completed.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@raymondproguy
raymondproguy merged commit 95d2cd4 into main Sep 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant