The fastfort command
Four commands — a signing key, a first account, a configuration report that gates a deploy, and a list of what the admin manages.
Available since v0.1.0
uv add "fastfort[cli]"
Four things a project needs before it can run an admin at all: a secret key, a first account to sign in with, a way to see whether the configuration is sound, and a way to see what is registered. Without the first two, a fresh install is unusable without writing a script.
--app
Every command that reaches your project takes it:
uv run fastfort check --app main:fort
module:attribute — where the FastFort instance lives. It is importable
because your main.py builds it at module scope.
generate-secret
uv run fastfort generate-secret
uv run fastfort generate-secret --export # FASTFORT_SECRET_KEY=…
uv run fastfort generate-secret --length 64
Printed and nothing else — never written to a file. A secret that a tool put on disk is a secret that ends up committed.
There is no default secret_key in FastFortSettings, because a framework that
ships one guarantees some deployment runs with it. The validator additionally
refuses a padded placeholder, anything under 32 characters, and anything using
fewer than 8 distinct characters.
createsuperuser
# Interactive
uv run fastfort createsuperuser --app main:fort
# CI
FF_PASSWORD='…' uv run fastfort createsuperuser \
--app main:fort \
--identity you@example.com \
--password-env FF_PASSWORD \
--no-input
--password-env rather than --password: a password passed as an argument is
visible in your shell history and in ps. Passing --password interactively
prints a warning saying so.
The command validates the password against auth.password_min_length and the
common-password list, refuses a duplicate identity, and writes only fields the
spec marks editable — so a user model missing one of the five conventional
columns is not a failure, the extra key is simply dropped.
check
uv run fastfort check --app main:fort
uv run fastfort check --app main:fort --deploy
Collects every problem instead of raising on the first, and exits non-zero — so it can gate a deployment:
Fastfort Freight · 17 model(s)
warn debug=True exposes tracebacks and internal state. Set FASTFORT_DEBUG=false.
warn security.cookie_secure=False sends the session cookie over plain HTTP.
Set FASTFORT_SECURITY__COOKIE_SECURE=true and serve over HTTPS.
warn auth.access_token_ttl is 7200s. A leaked access token stays valid that
long; 900s is the recommended ceiling.
3 problem(s) found.
Without --deploy it checks the wiring: a missing backend, a missing user model,
a model the backend cannot handle, an empty registry. With it, the production
ones as well.
- name: Configuration check
run: uv run fastfort check --app main:fort --deploy
env:
FASTFORT_SECRET_KEY: ${{ secrets.FASTFORT_SECRET_KEY }}
FASTFORT_DEBUG: "false"
registered-models
uv run fastfort registered-models --app main:fort
Accounts
accounts.user app.models.accounts.User
/admin/accounts.user/
accounts.role app.models.accounts.Role
/admin/accounts.role/
Catalog
catalog.product app.models.catalog.Product
/admin/catalog.product/
What the admin manages, where each model lives, and the URL of each page.
Useful when autodiscover did or did not find what you expected.
version
uv run fastfort version
Registration, explicitly or by discovery
fort.include_admin("app.admin") # named; an import error names the module
fort.autodiscover("app") # walks for every app/**/admin.py
autodiscover returns what it imported, and registered-models prints the
result — so what happened is always visible rather than magic. Modules that raise
during import are not suppressed.