How to create the first administrator of a fresh d-oauth install, and how
to replace that bootstrap account immediately. There is no HTTP bootstrap
endpoint: provisioning is CLI/Job only.
Task 48 owns
SECURITY.md; this file is the task-47 operator runbook. The threat model and the replace-immediately rationale live there.
d-oauth-server migrateInteractive (password read from stdin, never argv, never logged):
d-oauth-server admin bootstrap --email ops@example.com
Non-interactive / cluster Job (mounted secret file):
d-oauth-server admin bootstrap --email ops@example.com \
--password-file /run/secrets/bootstrap-password
When neither a file nor stdin yields a password, the command generates a
256-bit password and prints it exactly once, and only to a TTY. If stdout is
not a TTY the command refuses instead of risking the secret in Job logs:
re-run attached to a terminal or supply --password-file.
A second admin bootstrap refuses while any enabled admin exists:
d-oauth-server admin bootstrap --email second@example.com
# error: already exists: an administrator already exists (1); ...
The override requires explicit --force, prints a loud warning to stderr,
and emits an audited admin.bootstrap.force event. Prefer routine
provisioning instead:
d-oauth-server admin bootstrap --email second@example.com --force
d-oauth-server user create --admin --email named-admin@example.com
The bootstrap account starts with must_change_password=true and
totp_enabled=false. Its first login yields a restricted session that can
only change the password and enroll TOTP — /authorize, /oauth/*, and
admin-gated actions deny it until both steps complete.
/login/change-password.must_change_password and stamps password_changed_at./login/totp/setup and confirm with an authenticator
code. The session is re-issued with full authority only now.d-oauth-server user create --admin --email <name>@example.comd-oauth-server user disable --email ops@example.com/run/secrets/bootstrap-password material and revoke any copy of a
generated one-time password.On the audit target (never carrying secrets): admin.bootstrap.created
and admin.count.changed on success, admin.bootstrap.refused on refusal,
admin.bootstrap.force on a forced override. First-login, password-changed,
and TOTP-enrolled outcomes are emitted by the login flow itself.
migrate seeds zero users
and serve never creates one.