Configuration
Every Craft Easy setting can be tuned through environment variables or a .env file. Internally, settings are loaded by a Pydantic BaseSettings subclass, so values are validated, type-coerced, and fail loudly on startup when something is wrong.
How settings are loaded
Your project typically has its own Settings class that extends the Craft Easy base:
# src/my_project/settings.py
from craft_easy.settings import Settings as BaseSettings
class Settings(BaseSettings):
# Add your own project-specific settings here
PRODUCT_CATALOG_CACHE_TTL: int = 300
And wires it into create_base_app:
# src/my_project/app.py
from craft_easy import create_base_app
from my_project.settings import Settings
def create_app():
app = create_base_app(settings=Settings())
return app
Pydantic reads variables from the environment first, then from a .env file in the working directory (encoded as UTF-8, case-sensitive keys). Settings are frozen after startup; use get_settings() anywhere in the request pipeline to read them as a FastAPI dependency.
from fastapi import Depends
from craft_easy.settings import Settings, get_settings
@router.get("/me/preferences")
async def preferences(settings: Settings = Depends(get_settings)):
return {"currency": settings.DEFAULT_CURRENCY}
Required settings
Two fields have no default and must be supplied — the app will refuse to start without them:
| Setting |
Example |
Purpose |
DEFAULT_CURRENCY |
SEK, EUR, USD |
ISO 4217 currency code applied to money fields when the caller does not specify one. |
DEFAULT_VAT_RATE |
0.25 |
Decimal VAT rate applied to invoiced amounts when the product or line item does not override it. |
In production, JWT_PRIVATE_KEY and JWT_PUBLIC_KEY also become required because of a validator that runs when AUTH_ENABLED=true and ENVIRONMENT != "test".
App
| Setting |
Default |
Purpose |
APP_TITLE |
Craft Easy API |
Shown in the OpenAPI schema and on /docs. |
APP_VERSION |
1.0.0 |
Reported in the OpenAPI schema. |
ENVIRONMENT |
development |
One of development, test, production. Used by validators to loosen or tighten requirements. |
DEBUG |
false |
Verbose tracebacks in responses. Never enable in production. |
DOCS_ENABLED |
true |
Controls whether /docs and /openapi.json are served. |
Database
| Setting |
Default |
Purpose |
MONGODB_URI |
mongodb://localhost:27017 |
MongoDB connection string. Wrapped in SecretStr to prevent accidental logging. |
DATABASE_NAME |
craft_easy_dev |
Name of the database to use for all collections. |
Authentication
| Setting |
Default |
Purpose |
AUTH_ENABLED |
true |
Master switch. When false, every endpoint is open — useful for local development only. |
JWT_ALGORITHM |
ES512 |
JWT signing algorithm. |
JWT_PRIVATE_KEY |
— |
ES512 private key used to sign tokens. Required when AUTH_ENABLED=true outside of tests. |
JWT_PUBLIC_KEY |
— |
ES512 public key used to verify tokens. Required together with the private key. |
JWT_USER_EXPIRY_MINUTES |
30 |
Access token lifetime. |
JWT_REFRESH_MAX_LIFETIME_MINUTES |
43200 |
Refresh token maximum lifetime (30 days). |
Access control
| Setting |
Default |
Purpose |
ACCESS_CONTROL_ENABLED |
true |
Turns feature and attribute guards on or off. |
SYSTEM_USERS |
[] |
List of user IDs treated as system-level — bypass tenant scoping and see soft-deleted items. |
OTP
| Setting |
Default |
Purpose |
OTP_CODE_LENGTH |
6 |
Number of digits in a one-time code. |
OTP_EXPIRY_MINUTES |
10 |
How long a code stays valid. |
OTP_MAX_ATTEMPTS |
5 |
Maximum verify attempts before a code is invalidated. |
OTP_MAX_SENDS |
3 |
Maximum re-sends for the same recipient in a window. |
OTP_AUTO_CREATE_USER |
false |
When true, verifying an OTP for an unknown address creates the user. |
TOTP / 2FA
| Setting |
Default |
Purpose |
TOTP_ISSUER |
My App |
Issuer name embedded in the provisioning URI — shown in authenticator apps. |
TOTP_DIGITS |
6 |
Length of the TOTP code. |
TOTP_INTERVAL |
30 |
Time step in seconds. |
TOTP_VALID_WINDOW |
1 |
Accept codes ±N periods to tolerate clock drift. |
TOTP_BACKUP_CODE_COUNT |
10 |
Number of recovery codes generated per user. |
REQUIRE_2FA_FOR_ROLES |
[] |
Roles that must enrol in 2FA before they can authenticate. |
OAuth2
| Setting |
Default |
Purpose |
BASE_URL |
http://localhost:8000 |
Public URL used to construct OAuth2 redirect URIs. |
OAUTH2_PROVIDERS |
[] |
List of enabled providers (google, microsoft). |
OAUTH2_GOOGLE_CLIENT_ID |
— |
Google OAuth client ID. |
OAUTH2_GOOGLE_CLIENT_SECRET |
— |
Google OAuth client secret. |
OAUTH2_MICROSOFT_CLIENT_ID |
— |
Microsoft OAuth client ID. |
OAUTH2_MICROSOFT_CLIENT_SECRET |
— |
Microsoft OAuth client secret. |
Notifications
| Setting |
Default |
Purpose |
NOTIFICATION_DRY_RUN |
false |
When true, notifications are logged but never dispatched. |
SENDGRID_API_KEY |
— |
SendGrid API key for email delivery. |
SENDGRID_FROM_EMAIL |
noreply@example.com |
Default sender address. |
SENDGRID_FROM_NAME |
My App |
Default sender display name. |
ELKS_API_USERNAME |
— |
46elks username for SMS delivery. |
ELKS_API_PASSWORD |
— |
46elks password. |
ELKS_SENDER |
My App |
Default SMS sender name. |
FCM_PROJECT_ID |
— |
Firebase project ID for mobile push. |
FCM_SERVICE_ACCOUNT_JSON |
— |
Firebase service account JSON (as a string). |
Currency and tax
| Setting |
Default |
Purpose |
DEFAULT_CURRENCY |
required |
ISO 4217 currency code used when none is supplied. |
DEFAULT_VAT_RATE |
required |
Decimal VAT rate applied to taxable amounts by default. |
SEED_DEFAULT_ACCOUNTS |
true |
Seed a generic chart of accounts on startup when bookkeeping is enabled. |
Core features
| Setting |
Default |
Purpose |
AUDIT_LOG_ENABLED |
true |
Writes audit entries for every mutation. |
AUDIT_LOG_RETENTION_DAYS |
90 |
How long audit entries are kept before the cleanup job purges them. |
CASCADE_ENABLED |
true |
Applies cascade delete and update rules defined on models. |
ETAG_ENABLED |
true |
Enables ETag generation and If-Match enforcement. |
MULTI_TENANT_ENABLED |
true |
Applies tenant scoping to every query on TenantScopedDocument subclasses. |
Sub-tenants
| Setting |
Default |
Purpose |
SUB_TENANT_MAX_DEPTH |
3 |
Maximum hierarchy depth below the root tenant. |
SUB_TENANT_MAX_CHILDREN |
100 |
Maximum direct children per tenant. |
Optional modules
All feature-flagged modules are disabled by default. Turn them on only when you need them — the related routes, jobs, and models are unregistered when the flag is false.
| Setting |
Default |
Enables |
JOBS_ENABLED |
false |
API-side job framework (/jobs/* endpoints, job runner). |
JOBS_CLEANUP_DAYS |
90 |
Days of job run history kept by the cleanup job. |
JOBS_AUDIT_RETENTION_DAYS |
365 |
Audit retention used by the cleanup job. |
FILE_IMPORT_ENABLED |
false |
File import engine and pipelines. |
FILE_IMPORT_MAX_SIZE_MB |
50 |
Upload size ceiling for file imports. |
FILE_IMPORT_MAX_ERRORS |
100 |
Stops processing after this many row errors. |
FILE_IMPORT_PREVIEW_ROWS |
5 |
Rows returned by the dry-run preview endpoint. |
PAYMENTS_ENABLED |
false |
Payment providers, checkout, webhooks. |
BOOKKEEPING_ENABLED |
false |
Chart of accounts, ledger entries, journal endpoints. |
GDPR_ENABLED |
false |
GDPR export and erasure endpoints. |
BI_EXPORT_ENABLED |
false |
BI export job and handlers. |
WHITE_LABEL_ENABLED |
false |
Per-tenant branding and domain routing. |
WEBHOOKS_ENABLED |
false |
Outbound webhook delivery and event queue. |
WEBHOOK_EVENT_RETENTION_DAYS |
90 |
Days that delivered webhook events are retained. |
API key authentication
| Setting |
Default |
Purpose |
API_KEY_AUTH_MAX_PER_MINUTE |
10 |
Per-IP rate limit on API key auth attempts. |
API_KEY_AUTH_LOCKOUT_THRESHOLD |
5 |
Failed attempts before the key is locked. |
API_KEY_AUTH_LOCKOUT_MINUTES |
15 |
Lockout duration. |
Rate limiting
| Setting |
Default |
Purpose |
RATE_LIMIT_ENABLED |
true |
Master switch for request rate limiting. |
RATE_LIMIT_DEFAULT |
100/minute |
Default limit applied to most endpoints. |
RATE_LIMIT_AUTH |
10/minute |
Stricter limit for authentication endpoints. |
RATE_LIMIT_STORAGE_URI |
memory:// |
slowapi backend. Use redis://... in production. |
CAPTCHA
| Setting |
Default |
Purpose |
CAPTCHA_ENABLED |
false |
Enables Cloudflare Turnstile verification. |
CAPTCHA_SECRET_KEY |
— |
Turnstile secret key. |
CAPTCHA_SITE_KEY |
— |
Turnstile site key (exposed to clients). |
CAPTCHA_VERIFY_URL |
Cloudflare |
Turnstile verification endpoint. |
CAPTCHA_FAILED_ATTEMPTS_THRESHOLD |
3 |
Failed OTP attempts per IP before CAPTCHA is required. |
Abuse protection
| Setting |
Default |
Purpose |
BLOCK_DISPOSABLE_EMAILS |
true |
Rejects registrations from disposable email domains. |
OTP_RECIPIENT_MAX_PER_HOUR |
5 |
OTP sends per recipient per hour. |
OTP_RECIPIENT_MAX_PER_DAY |
10 |
OTP sends per recipient per day. |
MAX_DAILY_SMS |
10000 |
Global daily SMS cap across all tenants. |
MAX_DAILY_EMAIL |
50000 |
Global daily email cap across all tenants. |
DAILY_CAP_WARNING_THRESHOLD |
0.8 |
Emit a warning log at this fraction of the cap. |
AUTH_IP_MAX_PER_HOUR |
10 |
Auth attempts per IP per hour. |
AUTH_IP_MAX_PER_DAY |
30 |
Auth attempts per IP per day. |
Bulk operations
| Setting |
Default |
Purpose |
BULK_MAX_SIZE |
100 |
Maximum documents accepted per bulk request. |
Soft delete
| Setting |
Default |
Purpose |
SOFT_DELETE_ENABLED |
true |
Deleted documents are marked, not removed. |
SOFT_DELETE_RETENTION_DAYS |
90 |
Days before the purge job removes soft-deleted documents permanently. |
| Setting |
Default |
Purpose |
CURSOR_HMAC_SECRET |
change-me-in-production |
HMAC secret used to sign cursor tokens. Must be set to a strong value in production, otherwise cursors can be forged. |
CORS
| Setting |
Default |
Purpose |
CORS_ORIGINS |
["http://localhost:3000"] |
List of allowed origins. A validator rejects * when ENVIRONMENT=production. |
Metrics
| Setting |
Default |
Purpose |
METRICS_ENABLED |
true |
Exposes a Prometheus scrape endpoint. |
METRICS_PATH |
/metrics |
Path at which metrics are served. |
Health
| Setting |
Default |
Purpose |
HEALTH_CHECK_TIMEOUT_MS |
5000 |
Timeout for individual probes in /health/ready. |
Sentry
| Setting |
Default |
Purpose |
SENTRY_DSN |
— |
Enables Sentry when set. Leave empty to disable. |
SENTRY_ENVIRONMENT |
development |
Environment tag sent with every event. |
SENTRY_TRACES_SAMPLE_RATE |
0.1 |
Fraction of transactions traced. |
SENTRY_PROFILES_SAMPLE_RATE |
0.1 |
Fraction of transactions profiled. |
SENTRY_RELEASE |
— |
Release identifier for error attribution. |
Logging
| Setting |
Default |
Purpose |
LOG_LEVEL |
INFO |
Root logger level. |
LOG_FORMAT |
console |
console for human-readable, json for structured logs. |
LOG_SLOW_REQUEST_MS |
1000 |
Emit a warning log for any request slower than this. |
Validators
Two cross-field validators run at startup:
- Auth keys must be present. If
AUTH_ENABLED=true and ENVIRONMENT is anything other than test, both JWT_PRIVATE_KEY and JWT_PUBLIC_KEY must be set. Missing either raises a ValueError before the server accepts any requests.
- CORS wildcard blocked in production. If
ENVIRONMENT=production and CORS_ORIGINS contains *, startup fails.
Example .env
# App
APP_TITLE=My API
ENVIRONMENT=development
DEBUG=false
# Database
MONGODB_URI=mongodb://localhost:27017/?directConnection=true
DATABASE_NAME=my_api_dev
# Auth
AUTH_ENABLED=true
JWT_PRIVATE_KEY="-----BEGIN EC PRIVATE KEY-----\n..."
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n..."
# Currency
DEFAULT_CURRENCY=EUR
DEFAULT_VAT_RATE=0.25
# Cursor signing
CURSOR_HMAC_SECRET=replace-with-a-32-byte-random-string
# CORS
CORS_ORIGINS=["https://app.example.com"]
# Sentry
SENTRY_DSN=https://public@sentry.example.com/1
SENTRY_ENVIRONMENT=development
Source
craft-easy/craft-easy-api/src/craft_easy/settings.py — the full Settings class, every field definition, and the startup validators.