Removes the env-driven auth.Bootstrap() path that creates an "admin" user on first start with a stderr-printed password. With self-registration shipped (#376), the first user to register on a fresh instance becomes admin via CreateUserFirstAdminRace, so the bootstrap path is just a second source of truth that confuses operators and leaves an account in the DB nobody asked for.
Removed
internal/auth/bootstrap.go + its test
auth.Bootstrap() call from cmd/minstrel/main.go
AuthConfig + AdminBootstrapConfig structs from internal/config
MINSTREL_AUTH_ADMIN_USERNAME / MINSTREL_AUTH_ADMIN_PASSWORD env reads + their test
The auth: block from config.example.yaml
Bootstrap-related comments in docker-compose.yml and the README
New first-run flow
docker compose up
Visit http://<host>/register
Fill in your username + password — first user on an empty DB becomes admin automatically (existing behaviour, just now the only path)
The README quickstart text updated to reflect this.
Migration for existing instances
Operator stood the prod DB down for a fresh start, so this PR has no migration path baked in. For any future operator who upgrades with a bootstrap-admin row already in place: log in as the bootstrap admin, generate an invite from /admin/users, register a new admin via /register with that token, promote them, then delete the bootstrap account (last-admin guard requires the new admin to be promoted first).
Recovery story
Forgotten admin password on a single-admin instance is now only recoverable via Fable #321 (admin password reset CLI), which doesn't ship yet. Worth scheduling soon. Not blocking on this PR — the bootstrap path didn't help with forgotten passwords either (it only fired when users table was empty).
Default registration mode
Unchanged: registration_settings.mode still defaults to invite_only per migration 0022_user_management. After the operator registers as user #1, additional users need an invite token (generated via the new /admin/users Invites section). No UI yet for switching modes — filing as a small follow-up.
Test plan
CI runs Go tests + container build + Flutter checks.
Operator: from /admin/users, generate an invite, register a friend with that token, confirm friend lands as non-admin
Operator: confirm no MINSTREL_AUTH_ADMIN_* env var noise in logs / docker-compose / README
Removes the env-driven `auth.Bootstrap()` path that creates an "admin" user on first start with a stderr-printed password. With self-registration shipped (#376), the first user to register on a fresh instance becomes admin via `CreateUserFirstAdminRace`, so the bootstrap path is just a second source of truth that confuses operators and leaves an account in the DB nobody asked for.
## Removed
- `internal/auth/bootstrap.go` + its test
- `auth.Bootstrap()` call from `cmd/minstrel/main.go`
- `AuthConfig` + `AdminBootstrapConfig` structs from `internal/config`
- `MINSTREL_AUTH_ADMIN_USERNAME` / `MINSTREL_AUTH_ADMIN_PASSWORD` env reads + their test
- The `auth:` block from `config.example.yaml`
- Bootstrap-related comments in `docker-compose.yml` and the README
## New first-run flow
1. `docker compose up`
2. Visit `http://<host>/register`
3. Fill in your username + password — first user on an empty DB becomes admin automatically (existing behaviour, just now the only path)
The README quickstart text updated to reflect this.
## Migration for existing instances
Operator stood the prod DB down for a fresh start, so this PR has no migration path baked in. For any future operator who upgrades with a bootstrap-admin row already in place: log in as the bootstrap admin, generate an invite from `/admin/users`, register a new admin via `/register` with that token, promote them, then delete the bootstrap account (last-admin guard requires the new admin to be promoted first).
## Recovery story
Forgotten admin password on a single-admin instance is now only recoverable via Fable **#321** (admin password reset CLI), which doesn't ship yet. Worth scheduling soon. Not blocking on this PR — the bootstrap path didn't help with forgotten passwords either (it only fired when users table was empty).
## Default registration mode
Unchanged: `registration_settings.mode` still defaults to `invite_only` per migration `0022_user_management`. After the operator registers as user #1, additional users need an invite token (generated via the new `/admin/users` Invites section). No UI yet for switching modes — filing as a small follow-up.
## Test plan
CI runs Go tests + container build + Flutter checks.
- [ ] CI green
- [ ] Operator: deploy fresh, visit `/register`, register user #1, confirm `is_admin = true`
- [ ] Operator: from `/admin/users`, generate an invite, register a friend with that token, confirm friend lands as non-admin
- [ ] Operator: confirm no `MINSTREL_AUTH_ADMIN_*` env var noise in logs / docker-compose / README
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Removes the env-driven
auth.Bootstrap()path that creates an "admin" user on first start with a stderr-printed password. With self-registration shipped (#376), the first user to register on a fresh instance becomes admin viaCreateUserFirstAdminRace, so the bootstrap path is just a second source of truth that confuses operators and leaves an account in the DB nobody asked for.Removed
internal/auth/bootstrap.go+ its testauth.Bootstrap()call fromcmd/minstrel/main.goAuthConfig+AdminBootstrapConfigstructs frominternal/configMINSTREL_AUTH_ADMIN_USERNAME/MINSTREL_AUTH_ADMIN_PASSWORDenv reads + their testauth:block fromconfig.example.yamldocker-compose.ymland the READMENew first-run flow
docker compose uphttp://<host>/registerThe README quickstart text updated to reflect this.
Migration for existing instances
Operator stood the prod DB down for a fresh start, so this PR has no migration path baked in. For any future operator who upgrades with a bootstrap-admin row already in place: log in as the bootstrap admin, generate an invite from
/admin/users, register a new admin via/registerwith that token, promote them, then delete the bootstrap account (last-admin guard requires the new admin to be promoted first).Recovery story
Forgotten admin password on a single-admin instance is now only recoverable via Fable #321 (admin password reset CLI), which doesn't ship yet. Worth scheduling soon. Not blocking on this PR — the bootstrap path didn't help with forgotten passwords either (it only fired when users table was empty).
Default registration mode
Unchanged:
registration_settings.modestill defaults toinvite_onlyper migration0022_user_management. After the operator registers as user #1, additional users need an invite token (generated via the new/admin/usersInvites section). No UI yet for switching modes — filing as a small follow-up.Test plan
CI runs Go tests + container build + Flutter checks.
/register, register user #1, confirmis_admin = true/admin/users, generate an invite, register a friend with that token, confirm friend lands as non-adminMINSTREL_AUTH_ADMIN_*env var noise in logs / docker-compose / README