agentbbs/docs/admin.md
Anthony Ettinger 232b8151a2
M2: admin console over ssh admin@ (users, sessions, moderation, plugins) (#3)
A privileged operator console reached as `ssh admin@host`, gated by route
plus the $AGENTBBS_ADMINS allowlist (admin status is operator-granted only,
never self-assigned in-band). It is a self-contained Bubble Tea model, not a
hub plugin, so it never appears in the public menu.

Sections (PRD §6):
  - Users & members: list accounts; b = suspend/ban (operators protected).
    Banned accounts are blocked at the hub and pod@ routes.
  - Sessions & pods: live in-memory session registry; k = disconnect.
  - Moderation & audit: admin action log + agent@ transcripts (tab to switch).
  - Config & plugins: runtime snapshot; space = enable/disable a plugin
    (persisted; filtered from the hub on next sign-in).

Every privileged action is written to a new admin_actions audit table.

store: + banned column, admin_actions and plugin_state tables, and the
backing methods (ListUsers/SetBanned/RecentSessions/LogAdminAction/
RecentAdminActions/RecentChatsAll/DisabledPlugins/SetPluginDisabled), with
unit tests. auth: admin allowlist helpers + tests. Docs in docs/admin.md;
README M2 flipped to done.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 02:40:18 -07:00

81 lines
2.9 KiB
Markdown

# Admin console (M2)
The admin console is a privileged operator surface reached over SSH:
```
ssh admin@bbs.profullstack.com
```
It is **not** a hub plugin — it never appears in the public menu. Access is
gated by the route plus an operator allowlist, so a curious member who guesses
the route still gets nothing.
## Who is an admin
Admin status is granted **only by the operator**, out of band, via an
environment variable — it can never be self-assigned in-session:
```
AGENTBBS_ADMINS="anthony,ops" # comma/space-separated account names
```
To open the console you must:
1. connect as `admin@` (or `sysop@`), **and**
2. present the SSH key of an account whose name is in `$AGENTBBS_ADMINS`.
Anyone else gets `admin@ is restricted to operators.` and is disconnected.
## Sections
The console is a Bubble Tea TUI. Arrow keys (or `j`/`k`) move, `enter` opens a
section, `esc` goes back, `q` quits, `r` refreshes the current list.
### Users & members
Lists accounts (newest first) with kind / premium / verified flags.
- `b` — ban/unban the selected account. Banned accounts are blocked at login
on the hub and `pod@` routes. Operators cannot be banned.
### Sessions & pods
The **live** view of currently-connected SSH sessions (the in-memory registry,
distinct from the historical audit trail in the DB).
- `k` — disconnect the selected session (PRD §6 "terminate live sessions").
Your own session is marked `(you)` and is protected.
### Moderation & audit
- `tab` switches between the **admin action log** (every ban, kill, and plugin
toggle, with who/when) and recent **`agent@` transcripts** for review.
- Bans are taken from the Users section.
### Config & plugins
- Read-only runtime snapshot: host, sandbox mode, pods engine, mail status,
admin allowlist.
- `space` toggles a hub plugin on/off. The change is persisted (`plugin_state`
table) and takes effect on each member's next sign-in — disabled plugins are
filtered out of the hub menu.
## Audit trail
Every privileged action is written to the `admin_actions` table
(`admin, action, target, detail, created_at`) and is visible in the
Moderation & audit section. Connection metadata continues to land in the
`sessions` table as before.
## Persistence
| Concern | Where |
|--------------------|----------------------------------------|
| Suspensions | `users.banned` |
| Plugin enable/disable | `plugin_state(id, disabled)` |
| Admin action log | `admin_actions` |
| Session audit trail| `sessions` (historical) |
| Live sessions | in-memory registry (killable) |
## Not yet (future milestones)
- AgentAd ops (creative approval, ledgers) — lands with M5.
- Live operator takeover of an `agent@` chat — the transcripts are reviewable
here today; interactive takeover is future work.
- Per-plugin config editing beyond enable/disable.