docs: record D18 (admin bootstrap) and the deploy bootstrap step
The deploy README described how to start the container but not how to get into it, which left the first-run experience at a login page nobody can get past. Adds the actual command, both the interactive and the piped form, and says why there is no --password flag. D18 records the three decisions worth arguing with later rather than rediscovering: why this is a CLI instead of a bootstrap HTTP endpoint or an env var (both rejected, with reasons), why it refuses an existing email, why it is not restricted to the first user, and why the admin role is recorded but not yet enforced. Numbered D18 because D17 was taken by the registry-TLS decision that merged while this branch was in flight. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R2ZKeWkZV7ehf7fivrAkkG
This commit is contained in:
@@ -43,6 +43,41 @@ front of port 8080 — this container only ever serves plain HTTP itself.
|
||||
|
||||
`GET http://<host>:8080/api/v1/healthz` should return `{"status": "ok"}` once it's up.
|
||||
|
||||
## Create the first admin user
|
||||
|
||||
**A fresh deployment has no users and you cannot sign up for one.** Registration requires an invite
|
||||
code, invites are issued by an existing admin, and a new database has neither — so the first account
|
||||
is created from inside the container (`docs/DECISIONS.md` D18 for why it's a CLI and not a
|
||||
first-run web page):
|
||||
|
||||
```sh
|
||||
docker exec -it velodrome velodrome create-admin --email you@example.com
|
||||
```
|
||||
|
||||
That prompts for the password twice and prints the new account's id, email and role. Then log in at
|
||||
`VELODROME_PUBLIC_URL`. Note the **`-t`** — without a TTY there's nothing to prompt on; the command
|
||||
says so rather than hanging. Add `--name "Your Name"` to set a display name (it defaults to the part
|
||||
of the email before the `@`); it's editable in the UI later either way.
|
||||
|
||||
For a non-interactive run (a provisioning script), pipe the password in instead — note `-i` rather
|
||||
than `-it`:
|
||||
|
||||
```sh
|
||||
printf '%s' "$ADMIN_PASSWORD" | docker exec -i velodrome \
|
||||
velodrome create-admin --email you@example.com --password-stdin
|
||||
```
|
||||
|
||||
There is deliberately no `--password` flag: an argument would land in your shell history, in `ps`
|
||||
output, and in the Docker daemon's record of the exec'd command.
|
||||
|
||||
Re-run it with a different `--email` to add another admin. Re-running it with an email that already
|
||||
exists **refuses and changes nothing** — it is not a password-reset tool, and there isn't one yet
|
||||
(D18). Minimum password length is 8 characters, the same rule the register endpoint applies.
|
||||
|
||||
Nothing enforces the admin role yet — no admin-only endpoint exists — so today this differs from an
|
||||
invited account only in the role recorded on it. Invite management in a later phase is what starts
|
||||
reading it.
|
||||
|
||||
## Environment variables
|
||||
|
||||
All read by `apps/api/velodrome/config.py` (prefix `VELODROME_`) — the app and Alembic both read
|
||||
|
||||
Reference in New Issue
Block a user