Skip to content

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.

Cursor pagination

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:

  1. 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.
  2. 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.