> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alforse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> Environment variables for the Alforse API, grouped by concern.

This page documents `apps/api/.env.example`, the **SaaS distribution** template
(`EDITION=saas`). Validated by `apps/api/src/config/env.ts` — the API fails closed at boot in
production if a required secret is missing. Enterprise self-hosted and `enterprise_hybrid`
deployments use the same variable names but different infrastructure values; see
[Installation](/installation).

<Warning>
  Never commit real values for anything marked **Secret** below. Production secrets should live
  in Cloudflare Worker secrets, your container platform's secret store, or an equivalent
  customer-managed vault for enterprise self-hosted deployments.
</Warning>

## Edition and runtime

| Variable   | Secret | Notes                                                                           |
| ---------- | ------ | ------------------------------------------------------------------------------- |
| `EDITION`  |        | `saas` for this template. Self-hosted uses `self_hosted` / `enterprise_hybrid`. |
| `NODE_ENV` |        | `production` enables strict, fail-closed validation of every production secret. |
| `PORT`     |        | HTTP port for the Node/Docker runtime.                                          |

## Core infrastructure

System metadata and tenant data live in **two physically separate Postgres databases** — see
[Tenants & Data Boundaries](/concepts/tenants-and-platform). Each has an owner/admin URL (for
migrations) and a restricted app-role URL (for runtime queries).

| Variable                    | Secret | Notes                                                                                                        |
| --------------------------- | ------ | ------------------------------------------------------------------------------------------------------------ |
| `PLATFORM_DATABASE_URL`     | Yes    | Owner connection for system-metadata migrations.                                                             |
| `PLATFORM_APP_DATABASE_URL` | Yes    | Restricted runtime connection for system metadata.                                                           |
| `TENANT_DATABASE_URL`       | Yes    | Owner connection for tenant-plane migrations.                                                                |
| `TENANT_APP_DATABASE_URL`   | Yes    | Restricted runtime connection for the tenant plane; RLS-scoped by `app.tenant_id`.                           |
| `REDIS_URL`                 | Yes    | Sessions, rate limits, nonces. Blank falls back to in-memory (single process only — no real session revoke). |

## Auth

| Variable             | Secret | Notes                                                              |
| -------------------- | ------ | ------------------------------------------------------------------ |
| `JWT_ACCESS_SECRET`  | Yes    | Signs short-lived access tokens.                                   |
| `JWT_REFRESH_SECRET` | Yes    | Signs refresh tokens.                                              |
| `JWT_ISSUER`         |        | Default `alforse-api`.                                             |
| `JWT_AUDIENCE`       |        | Default `alforse-clients`.                                         |
| `JWT_ACCESS_TTL`     |        | Default `15m`.                                                     |
| `JWT_REFRESH_TTL`    |        | Default `30d`.                                                     |
| `API_HMAC_SECRET`    | Yes    | HMAC for signed, non-browser API requests; required in production. |

## Public URLs and CORS

| Variable                      | Secret | Production value                                                                                                                                                   |
| ----------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `API_PUBLIC_URL`              |        | `https://api.alforse.com/api/v1`                                                                                                                                   |
| `WEB_PUBLIC_URL`              |        | `https://deals.alforse.com`                                                                                                                                        |
| `DEALS_PUBLIC_URL`            |        | `https://deals.alforse.com`                                                                                                                                        |
| `CONSOLE_PUBLIC_URL`          |        | `https://console.alforse.com`                                                                                                                                      |
| `CORS_ALLOWED_ORIGINS`        |        | Comma-separated list of the origins above.                                                                                                                         |
| `PUBLIC_REGISTRATION_ENABLED` |        | Must be `false` in production unless your deployment explicitly supports public trial signup. Most production tenants use redemption codes or assisted onboarding. |

## Object storage (S3-compatible / Cloudflare R2)

| Variable         | Secret | Notes                                                                                                                                    |
| ---------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `OSS_ENDPOINT`   |        | S3-compatible endpoint. Alforse Cloud uses Cloudflare R2; enterprise self-hosted deployments use the approved customer-managed endpoint. |
| `OSS_BUCKET`     |        |                                                                                                                                          |
| `OSS_ACCESS_KEY` | Yes    |                                                                                                                                          |
| `OSS_SECRET_KEY` | Yes    |                                                                                                                                          |
| `OSS_REGION`     |        | `auto` in production (R2).                                                                                                               |

## OCR

| Variable                    | Secret | Notes                                                                                  |
| --------------------------- | ------ | -------------------------------------------------------------------------------------- |
| `OCR_ENABLED`               |        | Production: `true`.                                                                    |
| `OCR_PROVIDER`              |        | Production: `mistral`.                                                                 |
| `OCR_ENDPOINT`              |        | Production: `https://document-ai.alforse.com/extract` (the `apps/document-ai` Worker). |
| `OCR_API_KEY`               | Yes    |                                                                                        |
| `OCR_ALLOW_UNAUTHENTICATED` |        | Keep `false` for any customer-facing deployment.                                       |
| `OCR_TIMEOUT_MS`            |        | Default `60000`.                                                                       |

## License signing (self-hosted license issuance)

| Variable              | Secret | Notes                                                                                        |
| --------------------- | ------ | -------------------------------------------------------------------------------------------- |
| `LICENSE_PRIVATE_KEY` | Yes    | Ed25519 private key (PEM). Required when your deployment issues signed self-hosted licenses. |

## File scanning and download gating

| Variable                            | Secret | Notes                                                                                                                                                                  |
| ----------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FILE_SCAN_MODE`                    |        | `external` routes uploaded files to a scanning service before they're downloadable; this is the strict/safe default and the only supported mode for a real deployment. |
| `FILE_SCAN_ENDPOINT`                |        |                                                                                                                                                                        |
| `FILE_SCAN_API_KEY`                 | Yes    |                                                                                                                                                                        |
| `FILE_SCAN_TEMPORARY_BYPASS_ACK`    |        | Explicit acknowledgment flag for the temporary bypass.                                                                                                                 |
| `FILE_SCAN_ALLOW_SKIPPED`           |        |                                                                                                                                                                        |
| `FILE_DOWNLOAD_REQUIRES_CLEAN_SCAN` |        | Strict/safe default is `true`.                                                                                                                                         |

## MFA

| Variable                  | Secret | Notes                                                               |
| ------------------------- | ------ | ------------------------------------------------------------------- |
| `MFA_REQUIRED_FOR_ADMINS` |        | Production: `true` — admins must enroll TOTP MFA on first login.    |
| `MFA_SECRET_KEY`          | Yes    | Encrypts stored TOTP secrets; must be 32+ characters in production. |

## Turnstile (human verification)

| Variable                      | Secret | Notes                                                |
| ----------------------------- | ------ | ---------------------------------------------------- |
| `TURNSTILE_ENABLED`           |        | Production: `true`.                                  |
| `TURNSTILE_SECRET_KEY`        | Yes    | Must match the frontend site key.                    |
| `TURNSTILE_ALLOWED_HOSTNAMES` |        | Production: `deals.alforse.com,console.alforse.com`. |

<Note>
  Turnstile applies to public auth endpoints (register, redeem, login, password reset, invite
  accept). If you're calling those endpoints directly instead of through the Deals/Console UI,
  see [Authentication](/api-reference/authentication) for how `turnstileToken` fits into each
  request.
</Note>

## Data handling / AI gateway

| Variable                       | Secret | Notes                                                 |
| ------------------------------ | ------ | ----------------------------------------------------- |
| `EXTERNAL_DATA_MODE`           |        | Default `redacted`.                                   |
| `AI_GATEWAY_MODE`              |        | Default `inspect`.                                    |
| `AI_GATEWAY_ALLOWED_PROVIDERS` |        | Allowed provider identifiers for AI inspection flows. |
| `AI_GATEWAY_ALLOWED_MODELS`    |        | Allowed model identifiers for AI inspection flows.    |

## SSO (optional)

Configure providers in complete pairs — a half-configured pair is rejected in production, and a
missing pair simply hides that provider from `GET /auth/sso/providers`.

| Provider | Variables                                      |
| -------- | ---------------------------------------------- |
| Google   | `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`     |
| LinkedIn | `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET` |
| Slack    | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`       |
| DingTalk | `DINGTALK_APP_KEY`, `DINGTALK_APP_SECRET`      |
| Lark     | `LARK_APP_ID`, `LARK_APP_SECRET`               |

All `*_SECRET` / `*_APP_SECRET` values are secrets.

## Email

Transactional mail (password reset, invitations, MFA backup codes) defaults to the Resend HTTP
API — the only transport that's safe from the Worker runtime, which cannot open raw TCP
sockets.

| Variable                                                                                              | Secret                  | Notes                                                                                                                        |
| ----------------------------------------------------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `EMAIL_PROVIDER`                                                                                      |                         | `resend` for SaaS. `smtp` is self-hosted only.                                                                               |
| `RESEND_API_KEY`                                                                                      | Yes                     |                                                                                                                              |
| `EMAIL_FROM`                                                                                          |                         | Default `"Alforse <no-reply@notifications.alforse.com>"`.                                                                    |
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_SECURE` / `SMTP_STARTTLS` / `SMTP_USER` / `SMTP_PASS` / `SMTP_FROM` | `SMTP_PASS` is a secret | Self-hosted fallback only. With Resend's SMTP bridge: host `smtp.resend.com`, user `resend`, password = your Resend API key. |
