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.