CLI reference¶
Every sandloom command, the configuration tiers, accepted risks, the key settings
and the local CI runner. Run the commands from the application root (or
the framework repository root) with uv run sandloom .... Every command
has --help.
Context and declared architecture¶
Available in sandloom>=0.7.0a6,<0.8.
| Command | What it does |
|---|---|
sandloom context MODULE |
Compact Markdown module map; JSON with --format json. |
sandloom graph [MODULE] |
Compact project map or a detailed module neighborhood. |
sandloom graph impact ITEM |
Direct witnesses and potential transitive module impact. |
sandloom graph diff REV_OR_FILE |
Compare the committed or saved inspection index. |
sandloom graph sequence [ID] |
List readable process names or inspect saga/route phases; names and stable IDs are accepted. |
sandloom graph state ID |
Conditional saga state transitions. |
sandloom graph --format html > architecture.html |
Save the offline viewer. |
sandloom explore [MODULE] |
Generate, save and open the offline viewer. |
Both inspection commands accept --root PATH, --format and
--require-verified. Context formats are markdown|json. Graph formats are
text|json|mermaid|html; impact/diff exclude Mermaid. Graph defaults to text and
context to Markdown in terminals and pipes. JSON requires --format json.
The project text map includes module purposes and compact connection/count facts;
JSON includes stable module IDs and summaries. Results go to stdout; save them
with shell redirection.
Graph JSON accepts --detail compact|full and --max-bytes N (default 131072).
Full topology adds provenance/policy; full process queries add verification
basis evidence. graph MODULE --hops N --direction incoming|outgoing|both scopes
a neighborhood (default one hop, both directions). These flags apply only to
graph [MODULE]. Use module:NAME to disambiguate modules named impact, diff,
sequence or state.
A diff baseline is a project-relative saved file when it exists, ends in .json
or starts with ./; otherwise it is a local Git revision. Existing files win
over revision names. Revision comparison reads this project's committed index
without fetch, checkout or execution of old code. File comparison needs no Git.
Exit codes: 0 usable (check uncertainty), 1 invalid/missing/unsafe input, budget or
I/O failure, 2 usage (including an unknown graph module selector), 3 strict
verification unavailable. JSON emits one versioned
success/error envelope. Text errors leave stdout empty and explain on stderr.
Inspection performs no app imports, prompts, network access, .env loading or
implicit generation. See module maps and
graph semantics.
explore runs generate, writes .sandloom/cache/explore.html atomically, prints
its URL and opens the browser. --no-generate reads the saved index;
--no-open only saves. Module selectors, --root, --hops and --direction
work there too. Failed generation leaves the previous HTML intact and unopened.
Project and code¶
| Command | What it does |
|---|---|
sandloom new <app> [--with identity,tenancy,billing \| --no-batteries] [--module <name>] [--dir <path>] [--agents <a>,<b>] |
Generate a standalone, runnable application. --agents picks the coding agents it writes skills for (Agent tooling); at a terminal it asks, with the installed ones marked. The app depends on sandloom[redis,celery]>=0.7.0a6,<0.8 plus sandloom-<module>>=0.7.0a6,<0.8 per --with module. --framework-req, --framework-source {pypi,git,path} and --framework-path choose where the framework comes from (path: editable sources for exactly those distributions). |
sandloom add module <name> |
A new module under the app's modules package. It is discovered: nothing else to edit. |
sandloom add event <module> <name> [--field F] |
A typed Event in the module's events.py. |
sandloom add consumer <module> <channel> [--field F] |
An @on consumer of a channel (another module's event). |
sandloom add contract <module> <name> |
A @contract class and its DTO in the module's schemas.py; the stub and the DTO copy are generated. |
sandloom add saga <module> <name> |
A saga with a typed command step, plus tests. |
sandloom add projection <module> <name> |
A projection skeleton. |
sandloom add task <module> <name> [--periodic CRON] |
An idempotent task skeleton; --periodic adds its five-field cron beat schedule. |
sandloom add skills [--agents <a>,<b>] [--choose] [--force] |
Write the application's agent files for its coding agents: the skills in each agent's folder and the module-author contract. It updates a file it wrote earlier and keeps a file you edited unless --force. --agents or --choose changes the recorded choice in .sandloom/agent-files.json. |
sandloom add deploy <module>... [--service S] [--force] / --refresh / --check [--env-file F] |
Split into services: create (first run) or update the microservices profile and its gateway, derived from what the modules provide and consume; --check runs the split invariants (SANDLOOM-T014) and boots every service alone. Split an app into services. |
sandloom docs [<name>] [--write \| --check] |
Print a public sandloom.sdk name's exact signature, tier and docstring; --write / --check refresh or verify the generated reference blocks. |
sandloom explain <code> [--list \| --markdown] |
Explain an SANDLOOM-xxxx error code or a check rule: why, and the fix. |
sandloom version |
Print the framework version. |
Code generators run sandloom generate last, so snapshots and stubs are current
after scaffolding. add deploy validates deployment configuration; add skills
refreshes the agent files and does not run application generation.
sandloom add --root <path> runs a generator against another project root. Each
generator has a how-to: Add a module,
Add an event,
Add a consumer,
Add a contract,
Add a saga,
Add a projection.
Tests and gates¶
| Command | What it does |
|---|---|
sandloom generate [<module> ...] [--check] [--root ROOT] |
Writes every generated file: each module's module.snapshot.json, routes.snapshot.json and contracts.snapshot.json, the contract stubs and the DTO copies (<shared>/contracts/<module>_dto.py, a wire-shape copy of each provider DTO a contract signature names) and the exported event schemas. Every file is compared with the committed one first: an incompatible change (SANDLOOM-T001, T004, T005, T007, C012) fails and nothing is written. A weaker route policy is written only by sandloom routes snapshot --write. Module names keep those modules' files only. Loads the project's .env.test like sandloom test; generation does not apply migrations. Code generators run it last. sandloom new attempts it when dependencies are available; after uv sync, run it in the app environment. --check compares without writing; --root selects the application root. |
sandloom test [<module>] [--full] [--no-gates] [-n N] [--plan] [--ci] [--seed N \| --no-shuffle] |
The gate: the module's tests first, then the fast suite in parallel (-m "not slow" -n auto --dist loadfile, SANDLOOM_TEST_REDIS_DB_STRIDE=auto; --full includes slow tests), then ruff, ruff format, mypy, sandloom check, the architecture tests, lint-imports and the contract and route snapshots. A failing step names its code. It never writes a generated file: a stale one fails with "generated files are stale: run sandloom generate" and the list. --ci also runs ruff check --no-fix. The test steps run in a seeded random order (printed; --seed N replays it, --no-shuffle keeps file order): Test isolation. |
sandloom check [--rules FILE] [--only rule,...] [--list-rules] [--boot] |
Static architecture analyzers against ./architecture_rules.yml (exit 0 ok, 1 violations, 2 usage). --boot also builds the app through create_app and reports startup refusals and stale route snapshots. Outside this repository it needs pip install "sandloom[check]". |
sandloom routes snapshot [--write \| --check] [--module M] |
Each module's route snapshot (routes.snapshot.json: method, path, authentication, resolved policy). --check fails on a stale file (SANDLOOM-T001); after a route change run --write and review the diff. sandloom generate never writes a weaker policy (spec §5.8): --write is its explicit approval and names each weakening it approves. |
sandloom routes reference |
Print the generated route reference. |
sandloom contracts snapshot [--write \| --check] [--module M] |
Each module's contract snapshot (SANDLOOM-T004). |
sandloom contracts check --against <snapshot\|dir\|url> [--as provider\|consumer] [--service S] [--timeout T] |
Rolling-upgrade compatibility of this build's contracts against a snapshot or a running provider. |
Never re-run a serial pytest -q after each fix; sandloom test runs the suite in
parallel. The harness and its isolation guards are described in
Testing Modules; every fixture in the
testing reference.
docker compose -f deploy/compose/test.yml up -d --wait # test PostgreSQL :5433, Redis :6380
uv run python tools/checks/test_resources.py list|prune [--days 7] [--apply]
# shared test DBs / Redis indexes: owner, last use,
# connections; prune is a dry run without --apply and
# never touches a connected or recently used one
SANDLOOM_TEST_DATABASE_URL=postgresql+asyncpg://…/sandloom_test_mine uv run sandloom test
# run on YOUR OWN database: SANDLOOM_TEST_DATABASE_URL /
# SANDLOOM_TEST_CACHE_URL / SANDLOOM_TEST_BROKER_URL win over
# .env.test on every load path (a plain shell
# DATABASE_URL does NOT — .env.test overrides it)
uv run pytest src/sandloom/<module>/tests/test_x.py::test_y --no-cov # one test
Parallel workers get database <db>_gw<N> and their own Redis indexes, which
needs a Redis started with --databases 256. In tests, reload .env.test
with reload_test_env (from sandloom.sdk.testing), never a bare
load_dotenv(override=True). CI runs the slow suites on Python 3.11 and 3.12
only (3.13 / 3.14: -m "not slow"); the pre-push hook runs everything.
Migrations¶
Each module owns its own chain; the core chain holds the shared infrastructure tables. See Migrations and Write a migration.
| Command | What it does |
|---|---|
sandloom migrate new <module> "<message>" [--database D] [--rev-id R] |
Autogenerate a revision in ONE module's chain and add row-level security for each new TenantScoped table. The module's database must be at its chain head. |
sandloom migrate revision <module> -m "<message>" [--autogenerate] [--database D] [--rev-id R] |
A revision in one module's own chain (its migrations/, version table alembic_version_<chain>, its database). |
sandloom migrate upgrade-all [--apply] [--lock-timeout S] [--create-databases] |
Core chain → every hosting database, then each module's chain → its database (exit 1 on failure). Waits up to S (default MIGRATION_LOCK_TIMEOUT_SECONDS, 300) for another runner's advisory lock and logs the holder. --create-databases first creates a missing PostgreSQL database (role with CREATEDB; the microservices one-shots). |
sandloom migrate upgrade-core --database D --apply |
The core chain → a named database. |
sandloom migrate upgrade-module <module> --apply |
One module's chain → its database(s). |
sandloom migrate databases |
List the configured database instances. |
sandloom migrate plan |
Show the migration plan (dry run). |
sandloom migrate ensure-outbox --all-databases --apply |
DEV/REPAIR only: create the shared core tables (outbox, audit, consumer inbox, command log, HTTP idempotency log) per database. Refused in prod without --force (no drift detection) and the accepted risk unversioned_ddl_repair; production uses upgrade-core. |
sandloom migrate extract-module <module> --from <src> --to <dst> [--apply --i-have-stopped-writes] [--verify \| --drop-source \| --unfence [--force]] |
Move ONE module's existing data out of a shared database: fence, copy, verify, outbox/inbox hand-off, consumer-group owner transfer. Dry run by default; offline. Runbook: deploy/README.md. |
Operations¶
| Command | What it does |
|---|---|
sandloom outbox list-dead \| requeue \| purge [--database D] [--channel C] [--id ID \| --all] [--limit N] |
Inspect dead-lettered outbox events, return them to pending, or delete delivered events older than the retention window. |
sandloom streams dlq-list \| dlq-replay <channel> [--id ID] [--group G] |
List or re-publish a channel's Streams dead-letter entries. |
sandloom streams groups <channel> \| consumers [--all] \| release-group <channel> <group> |
Consumer groups of a channel, the subscription inventory, or forget a group's owner fingerprint. |
sandloom projection list \| rebuild <name> [--tenant <id>] |
List registered projections and consumers; rebuild one from its source. |
sandloom sagas list [--all] \| show <type> <id> \| retry <type> <id> \| park <type> <id> \| resolve <type> <id> --step S --as applied\|skipped |
Operate saga instances. Dry run unless --apply --reason R (prod: also --confirm-production). |
sandloom security revoke-sessions \| force-password-reset (--all \| --tenant ORG \| --user ID) [--operation-id ID] |
Incident response: end every session of the targets, or require a password reset. Dry run unless --apply --reason R (prod: also --confirm-production); --operation-id resumes an interrupted run. |
sandloom security grant-admin --email ADDRESS [--apply --reason R] [--confirm-production] |
Make a registered user a platform administrator and mark their address verified: how an application gets its first console operator. Dry run by default. |
sandloom security audit-export --since T [--until T] [--action-prefix P] --output FILE |
Export an audit-log window as JSONL (sanitized metadata). |
sandloom backup plan \| verify <dump> \| reconcile-security |
What to back up per database and Redis endpoint; restore a dump into a scratch database and check it; re-derive blocks and revocation marks after a security-cache restore. |
sandloom keys generate [--alg EdDSA\|RS256\|ES256] [--purpose jwt\|rpc\|rpc-dh] [--service S] [--kid K] [--out-dir DIR] |
Generate an access-token signing pair, or a service's internal-RPC keys. |
sandloom keys allowlist |
Derive RPC_ALLOWED_CALLS from SERVICE_BINDINGS. |
sandloom keys dev-env [--deploy-dir D] [--out F] |
DEVELOPMENT / CI ONLY: a compose --env-file with fresh keys for every variable the microservices profile requires. |
sandloom config dump [--json] [--only PREFIXES] [--non-default] [--show-secrets] |
The effective settings with their source (env, .env, SECRETS_DIR, default); secrets redacted unless --show-secrets (refused in prod). |
sandloom doctor [--json] [--skip S] [--timeout T] [--lag-warn N] |
Read-only deployment preflight: config, modules, infrastructure reachability, migration heads, consumer lag, RLS, sagas (exit 0 ok, 1 warnings, 2 failures). |
sandloom admin export --output FILE [--check] |
Compile the static admin UI manifest; --check fails on drift. |
Procedures: the runbook and the operator reference.
uv run uvicorn <app>.asgi:app --reload # API on :8000 (needs .env)
uv run celery -A sandloom.core.worker worker|beat --loglevel=INFO # background tasks
Framework dependency sources¶
Generate apps from an existing checkout with
uv run sandloom new myapp --framework-source path --framework-path ..
The path is the repository root containing packages/. Editable host paths
work locally; they are not portable Docker or CI dependencies.
--framework-source pypi installs published distributions and is the default
for the installed CLI. --framework-source git is disabled; use a local
checkout for source development. The canonical repository is
Sandloom-dev/sandloom. See
getting started.
Local CI (GitHub Actions stand-in)¶
Run from the repository root. It exports the ref with git archive (never
your working tree), runs the real ci.yml / docs.yml /
architecture-check.yml jobs under act where safe and the service-backed
jobs (backend-tests ×4, benchmarks-security, examples, microservices-smoke)
in its own fallback runner, each with fresh run-id-named Postgres/Redis on an
isolated network (never the shared sandloom_*_test containers). backend-tests
runs the full suite on Python 3.11 and 3.12 (coverage on 3.12); 3.13 and 3.14
run -m "not slow". It holds /tmp/sandloom-ci-local.lock while running (CPU
benchmarks wait for it); logs and summary in .ci-local/runs/<run-id>/;
exit 0 = green. Details: Local CI runner.
tools/ci/ci_local.sh <git-ref> [--fetch] [--jobs a,b] [--python 3.11,3.13] [--parallel N]
tools/ci/ci_local.sh pr:<N> # a PR head from origin
tools/ci/ci_local.sh <git-ref> --rerun-failed # only the jobs that failed last
# time for that ref (fail-fast loop; never a merge gate)
# jobs run in parallel by default (min(3, docker CPUs//3, mem//2.5GB));
# --parallel 1 = sequential
tools/ci/ci_local.sh --list | --check # jobs + engines | workflow drift check
# --report-pr <N> posts the summary on the PR via gh (only when asked)
uv sync installs the dev dependency group (ruff, mypy, pytest — pinned
exactly in pyproject.toml and locked in uv.lock); CI runs
uv sync --frozen and the hooks uv run --locked, so a stale uv.lock fails
instead of re-resolving. Bump a tool by editing its pin, uv lock, and fixing
its new findings in the same change.
Check database drift¶
uv run sandloom migrate drift builds a scratch database from the loaded
module migration chains and compares it with the models. It reports missing
or extra schema operations and does not repair the application database.
sandloom test includes this check. See migrations.
For incident tools (security, backup, streams, outbox, sagas, keys and
projections), including scope, dry-run, JSON, batching and production-confirmation
flags, use the operator reference.
Publishing the website is a separate manual workflow.
Configuration tiers and accepted risks¶
Every Settings field is classified in the kernel's tier table
(src/sandloom/core/config/tiers.py): semantic (changes a guarantee —
its value is chosen per profile dev / test / prod / prod +
multi_tenant_strict in one table, see
Configuration Tiers) or ops (capacity /
operations). A new setting must be classified
(src/sandloom/core/tests/test_config_tiers.py); prefer an ops knob or a
profile-chosen value over a new semantic flag — the semantic count is
ratcheted (ceiling 15) and a new one needs an ADR. Overrides of the profile
are logged at startup and listed by /diagnostics/core and sandloom doctor.
Every production weakening is an accepted risk, never a new boolean
(ADR-0006): SANDLOOM_ACCEPTED_RISKS (JSON or
comma list) names stable ids (fake_psp_in_prod, log_notifier_in_prod,
local_storage_in_prod, volatile_event_bus, unversioned_ddl_repair,
weak_password_hash, shared_hs_secret_split, relay_disabled,
command_dedupe_below_retry_horizon, saga_horizon_beyond_command_dedupe).
An unknown id fails startup, and each accepted risk is logged at every start,
exported as sandloom_accepted_risk_active{risk} and listed by
/diagnostics/core / sandloom doctor. Removed settings (among them the former
*_ALLOW_* booleans, RPC_AUTH_MODE, RPC_RESPONSE_BINDING and the former
per-route revocation settings) fail startup unless set to their inert value.
Key settings¶
Every knob is a Settings field (validated at startup, .env /
SECRETS_DIR-aware); the full list with defaults is the
Configuration Reference. Framework code never reads
configuration with os.getenv (SANDLOOM_COMPOSITION_ROOT is read from the
process env first, then from Settings / .env; only SANDLOOM_WORKER_TASKQUEUE
is raw env).
- Modules.
SANDLOOM_MODULES(JSON list) selects the modules a process loads; without it the composition root loads its defaults (ansandloom newapp: every module of itsmodulespackage plus the chosen module packages;sandloom.main:app: every installedsandloom.modulesentry point). An entry is a module name (identity) or an import path (sandloom_identity).MODULE_REQUIRED(load failure aborts startup; when empty, every module listed EXPLICITLY inSANDLOOM_MODULESis required — list the required subset inMODULE_REQUIREDinstead). See Loader Configuration. - Services.
SERVICE_BINDINGS(strictly validated;"required": truemakes/ready— never/health— fail while that peer is unreachable;SERVICE_PROBE_TIMEOUT_SECONDScaps each concurrent probe). - Events.
EVENT_BUS_BACKEND(auto/memory/redis/streams; auto = durablestreamsin prod,memoryelsewhere; the volatilememory/redis(Pub/Sub) drivers are refused in prod unless the riskvolatile_event_busis accepted; required/durable consumers on Pub/Sub also need it outside prod),OUTBOX_RELAY_ENABLED(auto/on/off/delegated; auto = prod only; under gunicorn only one elected worker runs it, the others getdelegated;offin a prod app process is refused unless the riskrelay_disabledis accepted),OUTBOX_RELAY_MAX_ATTEMPTS,OUTBOX_RELAY_BACKOFF_BASE_SECONDS/_MAX_SECONDS(per-row retry backoff),OUTBOX_RELAY_CIRCUIT_BASE_SECONDS/_MAX_SECONDS(broker-down cool-down that does not burn attempts),OUTBOX_RETENTION_HOURS(the relay purges delivered rows;sandloom outbox purgeon demand),CONSUMER_STARTUP_TIMEOUT_SECONDS,CONSUMER_RETRY_BASE_SECONDSandCONSUMER_RETRY_MAX_SECONDS(required-consumer startup/reconnect policy),EVENT_STREAM_READ_COUNT(Streams entries per consumed batch, default 64). - Contracts.
CONTRACT_READ_SCOPE_ENABLED/CONTRACT_READ_SCOPE_IDLE_MS(request-scoped read connection + memo for local read contracts; opt out of the memo per scope withcontract_read_scope(memo=False)fromsandloom.sdk.contracts),CONTRACT_DB_POOL_SIZE/CONTRACT_DB_MAX_OVERFLOW/CONTRACT_DB_LOCK_TIMEOUT_MS(dedicated pool of in-process contract calls),CONTRACT_COMMAND_DEDUPE_TTL_SECONDS(command replay window, at most 7 days). - Internal endpoints.
INTERNAL_API_TOKEN(unlocks/metrics,/diagnostics/*,/contracts/*and/healthdetails — never/_sandloom/rpc; without it they are closed, unlessINTERNAL_ENDPOINTS_OPEN_IN_DEV=trueoutside prod),METRICS_TOKEN(scrape-only/metricscredential; must differ from the internal token),HEALTH_CACHE_TTL_SECONDS. - Edge.
RATE_LIMIT_ENABLED(per-routerate_limiterlimits; always on in prod),RATE_LIMIT_GLOBAL_REQUESTS/RATE_LIMIT_GLOBAL_PERIOD_SECONDS/RATE_LIMIT_GLOBAL_EXEMPT_PATH_PREFIXES(opt-in per-IP floor over every route incl. 404s, 0 = off everywhere, fail-open; the primary floor is the edge gateway),TRUST_PROXY_HEADERS+TRUSTED_PROXY_IPS(CIDR list;X-Forwarded-Foris walked right-to-left from a trusted peer only —get_client_ipinsandloom.sdk.web),CORS_ORIGINS,MAX_UPLOAD_SIZE_BYTES,MAX_REQUEST_BODY_BYTES(global body cap → 413),DOCS_ENABLED(gate/docsetc.; off in prod by default). - Tokens and sessions.
JWT_ALGORITHM(HS256/384/512 shared secret — monolith/dev default — or EdDSA/RS256/ES256 asymmetric: the issuer — the process loading a module declaredissues_tokens=True, i.e. identity — holdsJWT_PRIVATE_KEY, every service verifies withJWT_PUBLIC_KEYS{"<kid>": "<PEM>"}and cannot mint; a critical startup check refuses a private key orJWT_SECRETon a verify-only process and, in prod, a shared HS secret in a split process unless the riskshared_hs_secret_splitis accepted;sandloom keys generatemakes a pair),JWT_PREVIOUS_SECRETS+JWT_KID(key rotation; asymmetric: rotate kids inJWT_PUBLIC_KEYS),AUTH_REVOCATION_FAIL_CLOSED,AUTH_REVOCATION_CLOCK_SKEW_SECONDS/AUTH_REVOCATION_WRITE_ATTEMPTS/AUTH_REVOCATION_RETRY_BASE_SECONDS(revocation mark margin and retries; identical in every service),TWOFA_ENCRYPTION_KEY(required in prod with identity; comma list = rotation),SECURITY_CACHE_INSTANCE(named cache for the subject-state records and — by default — idempotency records),IDEMPOTENCY_CACHE_INSTANCE(opt-in separate cache for idempotency records; empty = the security cache). Every authenticated route reads the session record live; there is no per-route revocation setting (Route policy). - Passwords.
PASSWORD_HASH_CONCURRENCY(argon2 pool; default min(4, CPUs)),PASSWORD_HASH_PROFILE(strongdefault = m=65536 KiB/t=3/p=4 |owasp_min= m=19456/t=2/p=1 |custom) +PASSWORD_HASH_TIME_COST/PASSWORD_HASH_MEMORY_KIB/PASSWORD_HASH_PARALLELISM(explicit values override the preset; build hashers withpassword_hasher()fromsandloom.sdk.security, never hand-pinned parameters; a successful login re-hashes a hash with other parameters; prod refuses parameters below the OWASP argon2id minimum unless the riskweak_password_hashis accepted). - Infrastructure.
INFRA(JSON declaring additional named infrastructure instances / driver overrides — see Capabilities & Infrastructure),DB_POOL_SIZE/DB_MAX_OVERFLOW/DB_POOL_TIMEOUT(default-engine pool sizing),DB_POOL_PING_IDLE_SECONDS(checkout ping only after that much idle time; nopool_pre_ping),DB_POOL_OVERFLOW_LINGER_SECONDS(overflow connections linger instead of churning connect/close; 0 = stock QueuePool),DB_POOL_FAIR_BARGE_MS(fair checkout: a waiter older than this is served before newcomers; 0 = strict FIFO handoff),DB_STATEMENT_TIMEOUT_MS/DB_IDLE_IN_TX_TIMEOUT_MS(server-side guards, 0 = off),CACHE_SOCKET_TIMEOUT_SECONDS/CACHE_SOCKET_CONNECT_TIMEOUT_SECONDS/CACHE_MAX_CONNECTIONS/CACHE_POOL_TIMEOUT_SECONDS/CACHE_RETRY_ATTEMPTS/CACHE_HEALTH_CHECK_INTERVAL_SECONDS(bounded Redis cache client: a dead Redis raises fast so fail-open/closed policies run),GC_FREEZE_AFTER_STARTUP/GC_THRESHOLDS/GC_METRICS_ENABLED(cyclic GC of gunicorn / Celery workers: freeze the startup heap, thresholds2000,10,10, pause histogram; applied by the process entry points, never bycreate_app). - Observability.
LOG_FORMAT(auto/json/text; auto = JSON in prod),LOG_LEVEL,SENTRY_DSN(+SENTRY_TRACES_SAMPLE_RATE; no-op when unset),TRACING_URL(OTLP endpoint). - Billing and notifications.
BILLING_PROVIDER=stripefails fast at startup unlessSTRIPE_SECRET_KEY+STRIPE_WEBHOOK_SECRETare set; prod refusesfakeunless the riskfake_psp_in_prodis accepted.NOTIFIER_BACKEND(log/smtp; default log) +SMTP_*(host/port/username/password/from/use_tls for the email driver). - Secrets. In prod, secrets and tokens must be at least 32 characters and
not placeholders.
SECRETS_DIR(Docker/K8s file-based secrets — a setting may be supplied as a file named after its field).
Secrets, backups, retention, audit and observability: the operator reference.