tenseleyflow/shithub / 47c480c

Browse files

docs: admin tour runbook for the operator's first-sign-in walkthrough

Authored by espadonne
SHA
47c480c7947b77ae2d8875c68f82307ec7f3bec2
Parents
a01b49f
Tree
0f378b5

1 changed file

StatusFile+-
A docs/internal/runbooks/admin-tour.md 128 0
docs/internal/runbooks/admin-tour.mdadded
@@ -0,0 +1,128 @@
1
+# Admin tour
2
+
3
+The site-admin surface — what's available, where to find it, and what
4
+each thing does. Audience: the first operator (you) signing in to a
5
+fresh production instance.
6
+
7
+## Becoming an admin
8
+
9
+The first user is not automatically an admin; the site treats the
10
+schema and the human as separate concerns so a self-signup never
11
+escalates itself. Promote yourself once via the CLI:
12
+
13
+```sh
14
+ssh root@shithub.sh
15
+sudo -u shithub /usr/local/bin/shithubd admin bootstrap-admin <username>
16
+```
17
+
18
+`bootstrap-admin` records an audit row with `actor_id = 0` so the
19
+"who promoted whom" history is never blank. After that, additional
20
+admins are toggled from the UI — see `/admin/users/<id>`.
21
+
22
+To stop being an admin, the same UI toggle works (or a peer admin can
23
+revoke you). There is intentionally no CLI demote: revocation should
24
+leave an audit row by an authenticated actor.
25
+
26
+## Web surface
27
+
28
+All admin routes live under `/admin/*` and are gated by
29
+`RequireSiteAdmin`. Non-admins get a 404, not a 403 — existence is
30
+not leaked.
31
+
32
+### Dashboard — `/admin`
33
+Counts: users, repos, orgs, jobs, admins. First glance to confirm the
34
+instance is alive and to spot anomalies (e.g., 10× job count overnight).
35
+
36
+### Users — `/admin/users` and `/admin/users/{id}`
37
+Lists users with suspension state and admin flag. The detail page has
38
+buttons for:
39
+- Suspend / unsuspend (writes audit, sends no email — quiet sanction).
40
+- Toggle site-admin (writes audit, irrevocable from CLI).
41
+- Force a password-reset link (1-hour token, mailed to primary email;
42
+  also surfaces in the journal if email backend is `stdout`).
43
+
44
+### Repos — `/admin/repos` and `/admin/repos/{id}`
45
+Filterable: deleted, archived. The detail page can force-archive or
46
+force-delete a repo. Hard-delete is two-step: first soft-delete (lands
47
+in the restore queue with a TTL), then purge after the TTL or via the
48
+restore-list page.
49
+
50
+### Jobs — `/admin/jobs` and `/admin/jobs/{id}`
51
+The worker queue. Filter by kind (`email_send`, `webhook_deliver`,
52
+`repo_archive_purge`, …) and status (`queued`, `running`, `failed`,
53
+`succeeded`). Detail page shows the JSON payload and the last error,
54
+with buttons to **Retry** (re-enqueue) and **Discard** (mark dead).
55
+First place to look when "the email never arrived" or "the webhook
56
+didn't fire."
57
+
58
+### Audit — `/admin/audit`
59
+Filterable history of security-relevant events: admin actions, login
60
+successes/failures, 2FA changes, key revocations, impersonation. All
61
+the filter dimensions live in the query string (`actor`, `action`,
62
+`target_type`, `target_id`, `since`, `until`). Bookmarkable.
63
+
64
+### System — `/admin/system`
65
+Runtime introspection: shithubd version + commit, Go runtime, DB
66
+connection-pool stats, repo-disk-usage rollup. Useful for "is this
67
+the version I just deployed?" — compare commit hash against
68
+`git log -1 origin/trunk`.
69
+
70
+### Email — `/admin/email`
71
+Outbound transactional email queue and last-N delivery results. If a
72
+verification or reset mail is missing, this is the second place to
73
+check (after `/admin/jobs` filtered to `email_send`).
74
+
75
+### Impersonation — `/admin/impersonate/{id}`
76
+Open a session as another user in **read-only** mode. All write
77
+endpoints reject with 403 from an impersonated session unless you
78
+explicitly switch to `/admin/impersonate/write-mode`. Both the start
79
+and the writable-promotion are audited.
80
+
81
+End impersonation with `/admin/impersonate/stop` (or via the banner
82
+that appears at the top of every page during an impersonation).
83
+
84
+## CLI subcommands
85
+
86
+Run as the system user that owns the binary's environment file. The
87
+canonical invocation is `sudo -u shithub /usr/local/bin/shithubd
88
+admin <subcommand>`; the binary reads `/etc/shithub/web.env` only
89
+when the systemd unit launches it, so for ad-hoc admin commands you
90
+either source that file first or rely on `shithubd`'s own env-only
91
+config (it'll print which knob is missing if so).
92
+
93
+| subcommand | purpose |
94
+|---|---|
95
+| `bootstrap-admin <username>` | Promote first user (chicken-and-egg). |
96
+| `reset-password <username>` | Issue a 1-hour password-reset link. |
97
+| `clear-2fa <username>` | Wipe TOTP enrollment (support escape hatch). User is emailed. |
98
+| `run-job <kind> [json-payload]` | Enqueue an arbitrary worker job. |
99
+| `recompute <metric>` | Recompute denormalized counters (`star_count`, `fork_count`). |
100
+
101
+`reset-password` and `clear-2fa` both leave audit rows attributed to
102
+`actor_id = 0` (system) so a recovered account always has an honest
103
+trail.
104
+
105
+## When to look where
106
+
107
+| Symptom | First stop |
108
+|---|---|
109
+| User says "didn't get my email" | `/admin/jobs?kind=email_send&status=failed` then `/admin/email` |
110
+| User locked out of 2FA | CLI `clear-2fa <user>` (then verify audit row) |
111
+| Suspicious activity from one IP | `/admin/audit?actor=<id>` + `fail2ban-client status shithubd-auth` on the droplet |
112
+| "Did this PR ship?" | `/admin/system` for the running commit; compare to `git rev-parse origin/trunk` |
113
+| Worker queue piling up | `/admin/jobs?status=queued` — sort by age, retry or discard the oldest |
114
+| Repo went missing | `/admin/repos?deleted=1` — soft-deleted lives in the restore queue |
115
+| Need to peek at a user's view | `/admin/impersonate/<id>` (read-only by default) |
116
+
117
+## What the admin surface deliberately does NOT have
118
+
119
+- **Bulk user actions.** No "suspend N users matching a filter."
120
+  Anything destructive at scale should be a thought-out script, not
121
+  one click.
122
+- **API endpoints.** Admin is HTML-only. Token-based admin actions
123
+  would let a leaked session token compromise the whole instance;
124
+  cookie sessions plus the per-action audit row keep the blast
125
+  radius bounded.
126
+- **Direct DB editing.** If you find yourself wanting it, the CLI
127
+  doesn't have it for a reason — write a migration or a one-off
128
+  job kind, then enqueue with `admin run-job`.