feat(auth): center-scoped single admin + GET /admin-email + cli #1

Merged
hzhang merged 2 commits from feat/admin-user into main 2026-05-22 21:59:15 +00:00
Contributor

Triage channels need to deliver every message to a single Center-scoped admin observer. Adds users.isAdmin column (synchronize:true auto-apply), Center cli (user set-admin/clear-admin/show-admin), and GET /auth/admin-email (api-key auth, returns {email,userId} or literal null).

set-admin uses a transaction filter on isAdmin=true (TypeORM rejects empty-where) to enforce at-most-one-admin.

Sim-verified end-to-end: cli set-admin → admin row updated → guild fetch returns correct shape.

🤖 Generated with Claude Code

Triage channels need to deliver every message to a single Center-scoped admin observer. Adds `users.isAdmin` column (synchronize:true auto-apply), Center cli (`user set-admin/clear-admin/show-admin`), and `GET /auth/admin-email` (api-key auth, returns `{email,userId}` or literal `null`). `set-admin` uses a transaction filter on `isAdmin=true` (TypeORM rejects empty-where) to enforce at-most-one-admin. Sim-verified end-to-end: cli set-admin → admin row updated → guild fetch returns correct shape. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hzhang added 2 commits 2026-05-22 21:59:11 +00:00
Triage channels in Guilds need to deliver every message to a single
"observer" admin user across the whole Center deployment (regardless of
on-duty / mention). This adds the data + auth surface for that.

## Schema

`users.isAdmin TINYINT DEFAULT 0` (synchronize:true auto-applies; cli
`user set-admin` enforces at-most-one in a transaction).

## CLI (subject `user`)

- `set-admin --email <e>` — clears every other admin row first, then
  marks the target. Returns `{admin: {email, userId}}`
- `clear-admin` — unsets all (returns `{cleared: N}`)
- `show-admin` — prints `{admin: {email, userId}}` or `{admin: null}`

## HTTP

`GET /auth/admin-email` (NOT @Public — requires a guild-node api key
via the existing CenterApiKeyGuard). Returns:
  - `{email: "...", userId: "..."}` if an admin exists
  - `null` (literal JSON) if no admin

Guild backends cache the result (1 day TTL per spec; with cli refresh
override on guild side).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hzhang merged commit cea3842c50 into main 2026-05-22 21:59:15 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: nav/Fabric.Backend.Center#1