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

# Environment variables

# Nora environment variable reference

> Complete reference for every environment variable Nora reads, grouped by category, with required flags, defaults, and configuration guidance.

Every Nora deployment is configured through environment variables. Start by copying `.env.example` to `.env` in your project root, then replace all `<REPLACE_...>` placeholders with real values before starting the stack. The sections below describe every variable Nora reads, grouped by function.

<img src="https://mintcdn.com/sttechnologyllc/hES1KHpUWDvLb_Wr/images/operator/settings-provider-setup.png?fit=max&auto=format&n=hES1KHpUWDvLb_Wr&q=85&s=23a104c14047005922e5708cb9c3ffdb" alt="Settings — the provider-keys surface that several environment variables ultimately drive" width="768" height="732" data-path="images/operator/settings-provider-setup.png" />

<Note>
  Generate cryptographic secrets with the following command. Never reuse secrets across deployments or commit them to version control.

  ```bash theme={null} theme={null}
  node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
  ```
</Note>

## Required

These variables must be set before the stack will start. Without them, JWTs cannot be signed, stored credentials cannot be encrypted, backup archives cannot be sealed, and Agent Hub source-catalog API keys cannot be hashed.

| Variable                             | Required | Default | Description                                                                                                                                                                                                                                                                                       |
| ------------------------------------ | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `JWT_SECRET`                         | Yes      | —       | Secret used to sign all JWTs. Minimum 32 characters. In production the backend refuses to boot when this is missing, too short, or looks like a placeholder (e.g. an unedited `.env.example` value). In development a generated secret is persisted in the database so sessions survive restarts. |
| `ENCRYPTION_KEY`                     | Yes      | —       | 32-byte hex key (64 hex characters) for AES-256-GCM encryption of stored provider keys, integration credentials, and other sensitive values. In production the backend refuses to boot when this is missing or invalid unless `NORA_ALLOW_PLAINTEXT_SECRETS=true` is set explicitly.              |
| `NORA_BACKUP_ENCRYPTION_KEY`         | Yes      | —       | 32-byte hex key for sealing managed backup archives. Required to create or restore backups. Do not rotate casually — archives encrypted with the previous key cannot be decrypted with a new one.                                                                                                 |
| `NORA_AGENT_HUB_API_KEY_HASH_SECRET` | Yes      | —       | HMAC secret for hashing Agent Hub source-catalog API keys at rest. `setup.sh` auto-generates this on first run; rotating it invalidates every issued Agent Hub key.                                                                                                                               |
| `NORA_API_KEY_HASH_SECRET`           | Yes      | —       | Primary HMAC secret for workspace/API token hashes. Setup preserves an existing value; when upgrading an older install it copies the legacy Agent Hub/encryption/JWT fallback so existing tokens remain valid.                                                                                    |
| `NORA_ALLOW_PLAINTEXT_SECRETS`       | No       | `false` | Explicit override to boot a production instance without a valid `ENCRYPTION_KEY` (credentials are then stored unencrypted). Only for throwaway/demo environments.                                                                                                                                 |

### Secret files

Production Compose and Helm deployments mount core credentials read-only under `/run/secrets`
instead of exposing them to frontend containers. The production control-plane entrypoint loads files
whose names are valid environment-variable names. A non-empty direct environment value takes
precedence; custom orchestrators can use these conventional aliases:

| Variable                                  | Default | Target environment variable          |
| ----------------------------------------- | ------- | ------------------------------------ |
| `JWT_SECRET_FILE`                         | —       | `JWT_SECRET`                         |
| `ENCRYPTION_KEY_FILE`                     | —       | `ENCRYPTION_KEY`                     |
| `NORA_BACKUP_ENCRYPTION_KEY_FILE`         | —       | `NORA_BACKUP_ENCRYPTION_KEY`         |
| `NORA_AGENT_HUB_API_KEY_HASH_SECRET_FILE` | —       | `NORA_AGENT_HUB_API_KEY_HASH_SECRET` |
| `NORA_API_KEY_HASH_SECRET_FILE`           | —       | `NORA_API_KEY_HASH_SECRET`           |
| `DB_PASSWORD_FILE`                        | —       | `DB_PASSWORD`                        |

The setup scripts keep `.env` owner-only: mode `0600` on macOS/Linux/WSL and a protected,
non-inherited ACL on Windows. Treat timestamped `.env.backup-*` files with the same sensitivity.
For Compose, setup also materializes the six core values as individual files under
`NORA_COMPOSE_SECRETS_DIR` (default `.secrets/compose`). The directory is gitignored and restricted
to the owner; Compose mounts only the requested files read-only into control-plane containers.
Operators who bypass setup must run `bash scripts/materialize-compose-secrets.sh <env-file>` before
Compose startup and after rotating any core value.

| Variable                   | Default            | Description                                                                                                   |
| -------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------- |
| `NORA_COMPOSE_SECRETS_DIR` | `.secrets/compose` | Host directory for installer-materialized Compose secret files. Must be a dedicated, non-symlinked directory. |
| `COMPOSE_PROJECT_NAME`     | `nora`             | Stable Compose namespace used to scope setup, upgrade, and clean-reinstall container/volume operations.       |

## Bootstrap admin account

These variables seed the first administrator account on the very first boot. They are ignored on subsequent starts once an account exists. Both must be set together. An empty hosted PaaS database requires secure explicit values and refuses to start without them; a self-hosted installation may leave both blank and claim the first administrator through `/signup`. Setting only one is always invalid.

| Variable                 | Required  | Default | Description                                                                                                                                           |
| ------------------------ | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DEFAULT_ADMIN_EMAIL`    | PaaS: Yes | —       | Valid, non-placeholder email address for the bootstrap admin account. Required when hosted PaaS starts with an empty user database.                   |
| `DEFAULT_ADMIN_PASSWORD` | PaaS: Yes | —       | Password for the bootstrap admin account. Minimum 12 characters; placeholders and default-derived values are rejected before the API listener starts. |

<Warning>
  Do not set a weak or well-known password here. This account has full operator access from the
  moment the database is seeded.
</Warning>

## Database

PostgreSQL connection settings. Defaults match the Docker Compose service name and port.

| Variable                            | Required | Default           | Description                                                                                                                                                                                                            |
| ----------------------------------- | -------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DATABASE_URL`                      | No       | —                 | Full PostgreSQL connection URL. Takes precedence over individual fields. URL `ssl=true`/`1` is normalized to verified TLS and `ssl=false`/`0` to disabled; explicit `DB_SSL_MODE`/`DB_SSL` overrides URL TLS controls. |
| `DB_HOST`                           | No       | `postgres`        | PostgreSQL hostname. Compose users keep the default; external DBs override.                                                                                                                                            |
| `DB_PORT`                           | No       | `5432`            | PostgreSQL port.                                                                                                                                                                                                       |
| `DB_USER`                           | No       | `nora`            | Database role used by backend-api and workers.                                                                                                                                                                         |
| `DB_PASSWORD`                       | Yes      | —                 | Password for the role above. For compose installs, keep this value aligned with the initialized Postgres volume; setup preserves it during reconfigure mode.                                                           |
| `DB_PASSWORD_FILE`                  | No       | —                 | Path to a file containing `DB_PASSWORD`. Production Compose sets this to `/run/secrets/DB_PASSWORD`; the direct value wins when both are non-empty.                                                                    |
| `DB_NAME`                           | No       | `nora`            | Database name.                                                                                                                                                                                                         |
| `DB_SSL_MODE`                       | No       | URL policy or off | TLS policy: `disable`, `require`, `verify-ca`, or `verify-full`. Blank honors `DATABASE_URL` SSL parameters; an explicit value overrides conflicting URL TLS parameters. Verification modes require a CA.              |
| `DB_SSL_CA`                         | No       | —                 | Inline PEM CA certificate; escaped `\\n` sequences are expanded.                                                                                                                                                       |
| `DB_SSL_CA_FILE`                    | No       | —                 | Path to a mounted CA certificate.                                                                                                                                                                                      |
| `DB_SSL_CERT`                       | No       | —                 | Inline client certificate for mutual TLS.                                                                                                                                                                              |
| `DB_SSL_CERT_FILE`                  | No       | —                 | Path to a mounted client certificate.                                                                                                                                                                                  |
| `DB_SSL_KEY`                        | No       | —                 | Inline client private key for mutual TLS.                                                                                                                                                                              |
| `DB_SSL_KEY_FILE`                   | No       | —                 | Path to a mounted client private key.                                                                                                                                                                                  |
| `DB_POOL_MAX`                       | No       | `20`              | Maximum PostgreSQL pool size per process.                                                                                                                                                                              |
| `DB_IDLE_TIMEOUT_MS`                | No       | `30000`           | Idle pooled-connection timeout.                                                                                                                                                                                        |
| `DB_CONNECTION_TIMEOUT_MS`          | No       | `10000`           | Connection acquisition timeout.                                                                                                                                                                                        |
| `DB_STATEMENT_TIMEOUT_MS`           | No       | `0`               | PostgreSQL statement timeout for normal application queries; `0` leaves it disabled.                                                                                                                                   |
| `DB_MIGRATION_LOCK_TIMEOUT_MS`      | No       | `60000`           | Maximum startup wait for Nora's migration advisory lock before failing closed. Clamped to 1 second–10 minutes.                                                                                                         |
| `DB_MIGRATION_STATEMENT_TIMEOUT_MS` | No       | `600000`          | Transaction-local timeout for migration ledger, legacy-repair, and migration statements. Clamped to 1 second–60 minutes.                                                                                               |

## Redis and queue

Redis backs BullMQ for deployment, ClawHub install, backup, and alert-delivery jobs.

| Variable                            | Required | Default   | Description                                                                                                                                                                                                      |
| ----------------------------------- | -------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `REDIS_URL`                         | No       | —         | Full Redis URL. Takes precedence over `REDIS_HOST` and `REDIS_PORT`.                                                                                                                                             |
| `REDIS_HOST`                        | No       | `redis`   | Redis hostname.                                                                                                                                                                                                  |
| `REDIS_PORT`                        | No       | `6379`    | Redis port.                                                                                                                                                                                                      |
| `REDIS_USERNAME`                    | No       | —         | Optional ACL username.                                                                                                                                                                                           |
| `REDIS_PASSWORD`                    | No       | —         | Optional Redis password. Set when Redis is exposed outside the compose network.                                                                                                                                  |
| `REDIS_DB`                          | No       | `0`       | Redis logical database index.                                                                                                                                                                                    |
| `REDIS_TLS`                         | No       | `false`   | Enables TLS for the Redis connection.                                                                                                                                                                            |
| `REDIS_TLS_CA`                      | No       | —         | Inline PEM CA certificate.                                                                                                                                                                                       |
| `REDIS_TLS_CA_FILE`                 | No       | —         | Path to a mounted Redis CA certificate.                                                                                                                                                                          |
| `REDIS_TLS_CERT`                    | No       | —         | Inline client certificate for mutual TLS.                                                                                                                                                                        |
| `REDIS_TLS_CERT_FILE`               | No       | —         | Path to a mounted Redis client certificate.                                                                                                                                                                      |
| `REDIS_TLS_KEY`                     | No       | —         | Inline client private key for mutual TLS.                                                                                                                                                                        |
| `REDIS_TLS_KEY_FILE`                | No       | —         | Path to a mounted Redis client private key.                                                                                                                                                                      |
| `REDIS_TLS_INSECURE_SKIP_VERIFY`    | No       | `false`   | Disables Redis certificate verification. Test-only; never use on an untrusted network.                                                                                                                           |
| `REDIS_CONNECT_TIMEOUT_MS`          | No       | `10000`   | Redis connection timeout.                                                                                                                                                                                        |
| `PORT`                              | No       | `4000`    | Backend-api listen port inside the container.                                                                                                                                                                    |
| `BACKEND_API_PORT`                  | No       | `4100`    | Host-only backend API port mapped to container `PORT`. Setup auto-picks another free port when `127.0.0.1:4100` is busy.                                                                                         |
| `DEPLOYMENT_WORKER_CONCURRENCY`     | No       | `6`       | How many deployment jobs the provisioner worker runs in parallel. Higher values reduce queued time but increase image-pull and bootstrap pressure.                                                               |
| `DEPLOYMENT_JOB_TIMEOUT_MS`         | No       | `900000`  | Timeout per deployment job, in milliseconds. Aliased by `PROVISION_TIMEOUT_MS` for legacy deployments.                                                                                                           |
| `CLAWHUB_INSTALL_TIMEOUT_MS`        | No       | `300000`  | Timeout per ClawHub skill install job, in milliseconds.                                                                                                                                                          |
| `HERMES_SKILLS_INSTALL_TIMEOUT_MS`  | No       | `300000`  | Timeout per Hermes skill install/uninstall job, in milliseconds.                                                                                                                                                 |
| `HERMES_SKILLS_REGISTRY_URL`        | No       | —         | Overrides the aggregated Hermes skills index URL (`https://hermes-agent.nousresearch.com/docs/api/skills-index.json`) used by the skills browse/search/detail API.                                               |
| `HERMES_SKILLS_INDEX_TTL_MS`        | No       | `3600000` | How long the fetched Hermes skills index is cached (in memory and on disk) before a refresh. When a refresh fails, the cached copy is served marked stale instead of failing browse requests.                    |
| `BACKUP_WORKER_CONCURRENCY`         | No       | `2`       | Backup worker concurrency.                                                                                                                                                                                       |
| `NORA_BACKUP_JOB_TIMEOUT_MS`        | No       | `1800000` | Backup job hard timeout. The backup worker enforces this via `Promise.race` because BullMQ v5 ignores `defaultJobOptions.timeout`.                                                                               |
| `NORA_BACKUP_SCHEDULE_POLL_MS`      | No       | `60000`   | How often the backup worker checks for due scheduled backups.                                                                                                                                                    |
| `AGENT_PROVISION_LOCK_TIMEOUT_MS`   | No       | `30000`   | Maximum wait for the shared per-agent provision lock used by replacement producers and destructive restore. On timeout Nora rejects the transition instead of racing an active worker.                           |
| `ALERT_DELIVERY_ATTEMPTS`           | No       | `5`       | Number of retry attempts for an alert webhook delivery before the worker records a terminal failure on the rule. Clamped to `[1, 10]`.                                                                           |
| `ALERT_DELIVERY_WORKER_CONCURRENCY` | No       | `5`       | Concurrency of the alert-deliveries worker.                                                                                                                                                                      |
| `WORKER_HEALTH_PORT`                | No       | `4001`    | Provisioner worker health-check port.                                                                                                                                                                            |
| `BACKUP_WORKER_HEALTH_PORT`         | No       | `4002`    | Backup worker health-check port.                                                                                                                                                                                 |
| `CLAWHUB_PRUNE_ORPHANED_SKILLS`     | No       | `false`   | When `true`, the provisioner reconciler uninstalls ClawHub skills present in a runtime container but not tracked in the agents table. Off by default — orphans are surfaced in the UI and removed explicitly.    |
| `HERMES_PRUNE_ORPHANED_SKILLS`      | No       | `false`   | When `true`, the provisioner reconciler uninstalls hub-installed Hermes skills present in a runtime but not tracked in the agents table. Off by default — orphans are surfaced in the UI and removed explicitly. |

## Access and URL

These variables control which nginx configuration is mounted, the listening port, and the public-facing base URL.

| Variable                    | Required | Default                 | Description                                                                                                                                                                                                            |
| --------------------------- | -------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NGINX_CONFIG_FILE`         | No       | `nginx.conf`            | Nginx configuration to mount. Use `nginx.conf` for local-only mode and `nginx.public.conf` for public-domain mode.                                                                                                     |
| `NGINX_HTTP_PORT`           | No       | `8080`                  | Host port mapped to the nginx HTTP listener. Use `8080` locally and `80` when deploying on a public domain. Setup auto-picks another free local port when `8080` is busy.                                              |
| `NEXTAUTH_URL`              | No       | `http://localhost:8080` | Canonical browser-facing URL of your Nora deployment. Set to your `https://` origin when exposing on a domain.                                                                                                         |
| `NORA_PUBLIC_URL`           | No       | —                       | Optional explicit public origin used by emails, OAuth callbacks, and audit links when it differs from `NEXTAUTH_URL`.                                                                                                  |
| `NORA_FORCE_SECURE_COOKIES` | No       | —                       | When set to `1`, forces the `Secure` flag on session cookies regardless of inbound scheme. Set for always-on-TLS public deployments behind a proxy/CDN that may strip `X-Forwarded-Proto`; leave empty for local HTTP. |

## API request rate limits

App-wide per-IP abuse protection sits in front of every route: a generous global budget for all
traffic plus a tighter budget for state-changing methods (POST/PUT/PATCH/DELETE). Overrides must be
positive integers; invalid values fall back to the secure defaults below. Raise them only where many
requests legitimately share one source IP — for example the Playwright E2E suite, which drives the
whole API from a single localhost address and otherwise self-throttles.

| Variable                        | Required | Default  | Description                                                            |
| ------------------------------- | -------- | -------- | ---------------------------------------------------------------------- |
| `GLOBAL_RATE_LIMIT_MAX`         | No       | `1000`   | All requests allowed per IP within the global window.                  |
| `GLOBAL_RATE_LIMIT_WINDOW_MS`   | No       | `900000` | Global rate-limit window in milliseconds (15 minutes).                 |
| `MUTATION_RATE_LIMIT_MAX`       | No       | `60`     | State-changing requests (POST/PUT/PATCH/DELETE) allowed per IP/window. |
| `MUTATION_RATE_LIMIT_WINDOW_MS` | No       | `60000`  | Mutation rate-limit window in milliseconds (1 minute).                 |

## Login abuse protection

Password and OAuth login share one per-IP rate-limit budget. Overrides must be positive integers;
invalid values fall back to the secure defaults below.

| Variable                    | Required | Default  | Description                                                       |
| --------------------------- | -------- | -------- | ----------------------------------------------------------------- |
| `AUTH_RATE_LIMIT_MAX`       | No       | `20`     | Combined password and OAuth login attempts allowed per IP/window. |
| `AUTH_RATE_LIMIT_WINDOW_MS` | No       | `900000` | Login rate-limit window in milliseconds (15 minutes).             |

## OAuth

OAuth login is disabled by default. Set `OAUTH_LOGIN_ENABLED=true` along with provider credentials to enable it. Provider verification must be completed in the relevant developer consoles before these values take effect.

| Variable               | Required | Default | Description                                                                        |
| ---------------------- | -------- | ------- | ---------------------------------------------------------------------------------- |
| `OAUTH_LOGIN_ENABLED`  | No       | `false` | Set to `true` to activate OAuth login.                                             |
| `GOOGLE_CLIENT_ID`     | No       | —       | OAuth 2.0 client ID from Google Cloud Console.                                     |
| `GOOGLE_CLIENT_SECRET` | No       | —       | OAuth 2.0 client secret from Google Cloud Console.                                 |
| `GITHUB_CLIENT_ID`     | No       | —       | OAuth App client ID from GitHub Developer Settings.                                |
| `GITHUB_CLIENT_SECRET` | No       | —       | OAuth App client secret from GitHub Developer Settings.                            |
| `NEXTAUTH_SECRET`      | No       | —       | Secret used by NextAuth.js for session encryption. Required when OAuth is enabled. |

## Public signup abuse protection

Signup remains public, but Nora bounds signup spikes with dedicated app-level rate limits and a runtime bot challenge. Self-hosted operators may explicitly opt out; hosted PaaS signup fails closed until Turnstile or reCAPTCHA is fully configured.

| Variable                            | Required  | Default    | Description                                                                                                                                              |
| ----------------------------------- | --------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SIGNUP_RATE_LIMIT_BURST_MAX`       | No        | `5`        | Maximum signup attempts per IP within the burst window.                                                                                                  |
| `SIGNUP_RATE_LIMIT_BURST_WINDOW_MS` | No        | `600000`   | Burst signup window in milliseconds.                                                                                                                     |
| `SIGNUP_RATE_LIMIT_DAILY_MAX`       | No        | `20`       | Maximum signup attempts per IP within the daily window.                                                                                                  |
| `SIGNUP_RATE_LIMIT_DAILY_WINDOW_MS` | No        | `86400000` | Sustained signup window in milliseconds.                                                                                                                 |
| `SIGNUP_BOT_PROTECTION_PROVIDER`    | PaaS: Yes | `none`     | Runtime challenge provider: `none`, `turnstile`, or `recaptcha`. Hosted PaaS signup fails closed when this is `none`; self-hosted operators may opt out. |
| `SIGNUP_TURNSTILE_SITE_KEY`         | No        | —          | Cloudflare Turnstile public site key. Required with the Turnstile provider and exposed through bootstrap status.                                         |
| `SIGNUP_TURNSTILE_SECRET`           | No        | —          | Cloudflare Turnstile verification secret. Required with the Turnstile provider and retained only by the backend.                                         |
| `SIGNUP_RECAPTCHA_SITE_KEY`         | No        | —          | Google reCAPTCHA public site key. Required with the reCAPTCHA provider and exposed through bootstrap status.                                             |
| `SIGNUP_RECAPTCHA_SECRET`           | No        | —          | Google reCAPTCHA verification secret. Required with the reCAPTCHA provider and retained only by the backend.                                             |

The signup page reads this configuration at runtime from `GET /api/auth/bootstrap-status`, so published frontend images do not need provider-specific build arguments. Existing `NEXT_PUBLIC_SIGNUP_BOT_PROTECTION_PROVIDER`, `NEXT_PUBLIC_SIGNUP_TURNSTILE_SITE_KEY`, and `NEXT_PUBLIC_SIGNUP_RECAPTCHA_SITE_KEY` values remain accepted as deprecated runtime aliases; setup migrates their values into the canonical names above.

<Warning>
  The reference production workflow refuses a `PLATFORM_MODE=paas` deployment unless the selected
  provider has both its public site key and server secret. This preflight runs before release
  metadata, secret materialization, image builds, or Compose replacement so a release cannot deploy
  public signup into a known unavailable state.
</Warning>

## Release tracking and upgrades

Drives the admin "release available" banner and the one-click upgrade runner. Setting `NORA_GITHUB_REPO` enables Nora to compare the running version against the latest published GitHub release.

| Variable                                    | Required | Default                                   | Description                                                                                                                                  |
| ------------------------------------------- | -------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `NORA_CURRENT_VERSION`                      | No       | —                                         | Current running version, set by your deploy pipeline.                                                                                        |
| `NORA_CURRENT_COMMIT`                       | No       | —                                         | Commit SHA of the running build.                                                                                                             |
| `NORA_GITHUB_REPO`                          | No       | `solomon2773/nora`                        | `owner/repo` used to query the GitHub Releases API.                                                                                          |
| `NORA_GITHUB_TOKEN`                         | No       | —                                         | Optional PAT to raise the GitHub API rate limit.                                                                                             |
| `NORA_RELEASE_CACHE_TTL_MS`                 | No       | `300000`                                  | How long to cache the latest-release lookup, in milliseconds.                                                                                |
| `NORA_LATEST_VERSION`                       | No       | —                                         | Manual override for the latest version. Skips the GitHub query when set.                                                                     |
| `NORA_LATEST_PUBLISHED_AT`                  | No       | —                                         | Manual override for the latest release timestamp.                                                                                            |
| `NORA_RELEASE_NOTES_URL`                    | No       | —                                         | URL displayed alongside the upgrade banner.                                                                                                  |
| `NORA_LATEST_SEVERITY`                      | No       | `warning`                                 | Severity of the upgrade banner. One of `info`, `warning`, `critical`.                                                                        |
| `NORA_UPGRADE_REQUIRED`                     | No       | `false`                                   | Set to `true` to display a hard-required upgrade banner.                                                                                     |
| `NORA_AUTO_UPGRADE_ENABLED`                 | No       | `false`                                   | Opt-in to one-click upgrades from Admin Settings.                                                                                            |
| `NORA_HOST_REPO_DIR`                        | No       | —                                         | Absolute Linux host path to this Nora checkout. Auto-filled by `setup.sh`.                                                                   |
| `NORA_UPGRADE_REPO`                         | No       | `https://github.com/solomon2773/nora.git` | Git URL used by the upgrade runner.                                                                                                          |
| `NORA_UPGRADE_REF`                          | No       | `master`                                  | Branch or ref the upgrade runner pulls.                                                                                                      |
| `NORA_UPGRADE_RUNNER_IMAGE`                 | No       | `docker:29-cli`                           | Container image the upgrade runner executes inside.                                                                                          |
| `NORA_UPGRADE_STATE_VOLUME`                 | No       | `nora_upgrade_state`                      | Docker volume that persists upgrade state between runs.                                                                                      |
| `NORA_ENV_FILE`                             | No       | `.env`                                    | Deploy env file passed to Docker Compose during one-click upgrades.                                                                          |
| `NORA_UPGRADE_COMPOSE_FILES`                | No       | auto-detected                             | Colon-separated compose file list used by the upgrade runner. Leave blank to reuse Docker Compose labels from the running backend container. |
| `NORA_UPGRADE_PUBLIC_HEALTH_URL`            | No       | —                                         | Optional public health URL checked after the stack rebuilds, for example `https://stage.example.com/api/health`.                             |
| `NORA_UPGRADE_HEALTHCHECK_ATTEMPTS`         | No       | `221`                                     | Attempts for setup and upgrade probes. At the 3-second default, attempt 221 runs after 660 seconds; maximum window is 3900 seconds.          |
| `NORA_UPGRADE_HEALTHCHECK_INTERVAL_SECONDS` | No       | `3`                                       | Seconds between those probes. Positive integers only; the combined first-to-final window must be at most 3900 seconds.                       |
| `NORA_INSTALL_METHOD`                       | No       | `source`                                  | Install method shown in admin UI. One of `source`, `docker_image`, `custom`.                                                                 |
| `NORA_MANUAL_UPGRADE_COMMAND`               | No       | `./setup.sh --update`                     | Command displayed when auto-upgrade is disabled.                                                                                             |
| `NORA_MANUAL_UPGRADE_STEPS`                 | No       | —                                         | Optional override for the displayed manual-upgrade steps.                                                                                    |

The former built-in `40` attempts / `3` seconds pair is treated as a legacy default and upgraded to `221` / `3`; other valid custom pairs are preserved.

## System banner

| Variable                     | Required | Default | Description                                                                                                                             |
| ---------------------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `NORA_SYSTEM_BANNER_ENABLED` | No       | `false` | Master switch for the admin-configured banner shown across `/app` and `/admin`. Banner copy and severity are managed in Admin Settings. |

## Platform mode

| Variable        | Required | Default      | Description                                                                                                                                                                                                                                                                                                             |
| --------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PLATFORM_MODE` | No       | `selfhosted` | Operating mode. `selfhosted` uses operator-defined request limits with no billing. `paas` enables Stripe billing; plan tiers control agent-count and managed-backup entitlements while per-agent compute uses operator-managed platform defaults. See [Platform modes](/configuration/platform-modes) for full details. |

## Self-hosted resource limits

These variables are only read when `PLATFORM_MODE=selfhosted`. They define the maximum resources any single user can request when deploying an agent.

| Variable      | Required | Default | Description                                                                           |
| ------------- | -------- | ------- | ------------------------------------------------------------------------------------- |
| `MAX_VCPU`    | No       | `16`    | Maximum vCPUs a user may allocate to a single agent.                                  |
| `MAX_RAM_MB`  | No       | `32768` | Maximum RAM in megabytes a user may allocate to a single agent.                       |
| `MAX_DISK_GB` | No       | `500`   | Maximum requested `disk_gb`; actual storage enforcement depends on the deploy target. |
| `MAX_AGENTS`  | No       | `50`    | Maximum number of agents a single user may deploy.                                    |

## Billing / Stripe

These variables are only read when `PLATFORM_MODE=paas`. See [Platform modes](/configuration/platform-modes) for a full explanation of PaaS mode.

| Variable                  | Required | Default | Description                                                                                                |
| ------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| `BILLING_ENABLED`         | No       | `false` | Set to `true` to enforce Stripe subscription limits. When `false` in PaaS mode, deployments are unlimited. |
| `STRIPE_SECRET_KEY`       | No       | —       | Stripe secret key. Use `sk_test_...` for testing and `sk_live_...` for production.                         |
| `STRIPE_WEBHOOK_SECRET`   | No       | —       | Stripe webhook signing secret (`whsec_...`) for verifying incoming webhook events.                         |
| `STRIPE_PRICE_PRO`        | No       | —       | Stripe Price ID (`price_...`) for the Pro subscription tier.                                               |
| `STRIPE_PRICE_ENTERPRISE` | No       | —       | Stripe Price ID (`price_...`) for the Enterprise subscription tier.                                        |

## Runtime selection

Runtime selection is three-dimensional: **runtime family × deploy target × sandbox profile**. Each enabled-list variable accepts a comma-separated list. The first id in each list is the default for new deployments. See [Provisioner backends](/configuration/provisioner-backends) for details.

| Variable                   | Required | Default     | Description                                                                                                                                                                                          |
| -------------------------- | -------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ENABLED_RUNTIME_FAMILIES` | No       | `openclaw`  | Runtime families enabled. Supported ids: `openclaw`, `hermes`.                                                                                                                                       |
| `ENABLED_BACKENDS`         | No       | `docker`    | Env-configured deploy targets enabled. `proxmox` is opt-in and experimental. Kubernetes clusters are registered in Admin.                                                                            |
| `ENABLED_SANDBOX_PROFILES` | No       | `standard`  | Sandbox profiles enabled. Supported ids: `standard`, `nemoclaw`.                                                                                                                                     |
| `DOCKER_AGENT_BIND_IP`     | No       | `127.0.0.1` | Host address used for Docker-published agent gateway/runtime ports. Keep loopback unless trusted remote clients must connect; protect any wider bind with a firewall or authenticated reverse proxy. |

## Kubernetes registry and Compose mount helpers

Nora no longer creates Kubernetes execution targets from `.env` labels. Register every Kubernetes cluster in **Admin -> Kubernetes**. Each enabled row becomes a concrete execution target such as `k8s:aks-eastus2` and stores its provider, actual cluster name, kubeconfig, namespaces, exposure mode, Service annotations, load-balancer source ranges, and load-balancer class.

`docker-compose.kubernetes.yml` only mounts a kubeconfig directory into the Nora containers. Put one or more kubeconfig files under `NORA_KUBECONFIGS_DIR`, then enter `/kubeconfigs/<file>` as each Admin **Kubeconfig path**. The Admin path must be the path visible inside `backend-api` and `worker-provisioner`, not the host path.

For more than one cluster, put multiple kubeconfig files under `NORA_KUBECONFIGS_DIR`. The Kubernetes overlay mounts that directory at `/kubeconfigs`, so two AKS clusters can use Admin paths such as `/kubeconfigs/aks-eastus2` and `/kubeconfigs/aks-westus2` while both still run through `docker-compose.kubernetes.yml`.

| Variable                    | Required | Default                               | Description                                                                                             |
| --------------------------- | -------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `NORA_KUBECONFIGS_DIR`      | No       | `./.secrets/kubeconfigs`              | Host directory mounted at `/kubeconfigs` by the Kubernetes overlay for single- or multi-cluster setups. |
| `CONTAINER_KUBECONFIG_PATH` | No       | `/tmp/nora-kind.container.kubeconfig` | Kind smoke helper file; its parent directory is mounted as `NORA_KUBECONFIGS_DIR`.                      |

Admin-registered encrypted kubeconfigs require `ENCRYPTION_KEY` because Nora stores pasted kubeconfig content encrypted at rest.

## Docker agent published ports

Docker and remote-Docker agents reserve their published gateway ports in PostgreSQL. The configurable subrange must remain inside `19000-19999`, which is already allowed by the gateway proxy's default SSRF guard. Local Docker deployments skip ports published by running containers and retry a bounded number of bind conflicts, including conflicts caused by non-Docker listeners.

| Variable                      | Required | Default | Description                                                                                    |
| ----------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------- |
| `DOCKER_AGENT_PORT_RANGE_MIN` | No       | `19000` | Lowest published gateway port. Must be between `19000` and `19999`.                            |
| `DOCKER_AGENT_PORT_RANGE_MAX` | No       | `19999` | Highest published gateway port. Must be between `19000` and `19999` and not below the minimum. |

## Gateway proxy SSRF guard

Defaults allow OpenClaw's internal gateway port, Docker-published 19000-19999, and Kubernetes NodePort 30000-32767. Override only if you publish gateways on non-default ports or hosts.

| Variable                           | Required | Default | Description                                                                |
| ---------------------------------- | -------- | ------- | -------------------------------------------------------------------------- |
| `NORA_GATEWAY_PROXY_ALLOWED_PORTS` | No       | —       | Comma-separated list of additional ports the gateway proxy may target.     |
| `NORA_GATEWAY_PROXY_ALLOWED_HOSTS` | No       | —       | Comma-separated list of additional hostnames the gateway proxy may target. |

## Remote Docker safety and authorization

Active Remote Docker gateway, log, and terminal streams periodically re-check that the agent owner
still owns the host or has an `editor`, `admin`, or `owner` workspace grant. Lower values reduce the
window before those streams close after revocation, at the cost of more authorization queries.
Remote-host create, edit, Test, SSH-pin reset, delete, share, and unshare operations are serialized
per host id so concurrent ownership, grant, trust, or credential mutations cannot race across
backend replicas.

| Variable                               | Required | Default | Description                                                                                                                                          |
| -------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `REMOTE_HOST_AUTH_RECHECK_MS`          | No       | `1000`  | Grant recheck interval in milliseconds for active gateway, log, and terminal streams. Minimum 250.                                                   |
| `REMOTE_HOST_MUTATION_LOCK_TIMEOUT_MS` | No       | `15000` | Maximum wait for another create/edit/Test/pin-reset/delete/share/unshare operation on the same host id. Timeout rejects the competing request (409). |

## Proxmox

Proxmox is an opt-in experimental LXC target for standard OpenClaw and prepared Hermes images. Keep Docker enabled while validating it, use verified API TLS and pinned SSH host keys, and run the real-host smoke before production use. NemoClaw on Proxmox remains blocked.

| Variable                                   | Required            | Default                                                    | Description                                                                                                                                                                                                                                                                                                       |
| ------------------------------------------ | ------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PROXMOX_API_URL`                          | With target         | —                                                          | HTTPS Proxmox API origin or URL ending in `/api2/json`.                                                                                                                                                                                                                                                           |
| `PROXMOX_TOKEN_ID`                         | With target         | —                                                          | API token ID in `user@realm!tokenname` format.                                                                                                                                                                                                                                                                    |
| `PROXMOX_TOKEN_SECRET`                     | With target         | —                                                          | Secret associated with the API token.                                                                                                                                                                                                                                                                             |
| `PROXMOX_VERIFY_TLS`                       | No                  | `true`                                                     | Verifies the Proxmox API certificate. `false` is test-only.                                                                                                                                                                                                                                                       |
| `PROXMOX_CA_CERT` / `PROXMOX_CA_CERT_PATH` | No                  | —                                                          | Inline or mounted private CA certificate.                                                                                                                                                                                                                                                                         |
| `PROXMOX_ALLOW_INSECURE_HTTP`              | No                  | `false`                                                    | Test-only opt-in for an isolated HTTP fixture.                                                                                                                                                                                                                                                                    |
| `PROXMOX_NODE`                             | No                  | `pve`                                                      | Proxmox node where LXCs are created.                                                                                                                                                                                                                                                                              |
| `PROXMOX_TEMPLATE`                         | No                  | `local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst` | LXC template used for OpenClaw agents.                                                                                                                                                                                                                                                                            |
| `PROXMOX_HERMES_TEMPLATE`                  | For Hermes          | —                                                          | Prepared LXC template containing the Hermes executable and `hermes` user.                                                                                                                                                                                                                                         |
| `PROXMOX_ROOTFS_STORAGE`                   | No                  | `local-lvm`                                                | Storage pool used for the rootfs.                                                                                                                                                                                                                                                                                 |
| `PROXMOX_BRIDGE`                           | No                  | `vmbr0`                                                    | DHCP-capable network bridge attached to created LXCs.                                                                                                                                                                                                                                                             |
| `PROXMOX_SSH_HOST` / `PROXMOX_SSH_USER`    | With target         | — / `root`                                                 | SSH endpoint used to run `pct`.                                                                                                                                                                                                                                                                                   |
| `PROXMOX_SSH_PORT`                         | No                  | `22`                                                       | SSH port.                                                                                                                                                                                                                                                                                                         |
| `PROXMOX_SSH_PRIVATE_KEY` / `_PATH`        | One SSH auth method | —                                                          | Inline or mounted SSH private key.                                                                                                                                                                                                                                                                                |
| `PROXMOX_SSH_PRIVATE_KEY_PASSPHRASE`       | No                  | —                                                          | Passphrase for the configured private key.                                                                                                                                                                                                                                                                        |
| `PROXMOX_SSH_PASSWORD`                     | One SSH auth method | —                                                          | Password fallback when key auth is not configured.                                                                                                                                                                                                                                                                |
| `PROXMOX_SSH_HOST_FINGERPRINT`             | With target         | —                                                          | Trusted OpenSSH SHA-256 host-key fingerprint.                                                                                                                                                                                                                                                                     |
| `PROXMOX_SSH_INSECURE_ACCEPT_HOST_KEY`     | No                  | `false`                                                    | Test-only bypass for pinned host verification.                                                                                                                                                                                                                                                                    |
| `PROXMOX_PCT_COMMAND` / `PROXMOX_SUDO`     | No                  | `pct` / auto                                               | `PROXMOX_PCT_COMMAND` accepts one command name or absolute executable path with no arguments. `PROXMOX_SUDO` accepts only `sudo -n` or an absolute `sudo` path followed by `-n`; other values fail preflight.                                                                                                     |
| `PROXMOX_OFFLINE_STAGE_COMMAND`            | Non-root SSH only   | —                                                          | One absolute path to the operator-installed strict stopped-LXC staging helper. Missing or unsafe values keep a non-root target unavailable; arguments, traversal, and shell syntax are rejected. See the [helper contract](/configuration/provisioner-backends/proxmox#non-root-offline-staging-helper-contract). |
| `PROXMOX_NODE_MAJOR`                       | No                  | `24`                                                       | Minimum Node.js major installed in stock OpenClaw templates.                                                                                                                                                                                                                                                      |
| `PROXMOX_OPENCLAW_PACKAGE`                 | No                  | `openclaw@2026.6.11`                                       | Package spec installed for OpenClaw. Override only after validating Gateway and activation compatibility.                                                                                                                                                                                                         |
| `PROXMOX_HERMES_BIN`                       | No                  | `/opt/hermes/.venv/bin/hermes`                             | Hermes executable in the prepared template.                                                                                                                                                                                                                                                                       |
| `PROXMOX_HERMES_ENABLE_INSECURE_DASHBOARD` | No                  | `false`                                                    | Binds the Hermes dashboard to `0.0.0.0` instead of `127.0.0.1`. The dashboard now always requires authentication (Hermes basic-auth provider; Nora injects a per-agent credential), so this controls only the bind address — not authentication. Leave false unless the port is separately protected.             |

## NemoClaw / NVIDIA

Read when `ENABLED_SANDBOX_PROFILES` includes `nemoclaw`.

| Variable                 | Required | Default                                          | Description                                                                                                                                                                                                                        |
| ------------------------ | -------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NVIDIA_API_KEY`         | No       | —                                                | API key from [build.nvidia.com](https://build.nvidia.com) for accessing NVIDIA-hosted Nemotron models.                                                                                                                             |
| `NEMOCLAW_DEFAULT_MODEL` | No       | `nvidia/nemotron-3-super-120b-a12b`              | Default Nemotron model used by NemoClaw agents.                                                                                                                                                                                    |
| `NEMOCLAW_SANDBOX_IMAGE` | No       | `ghcr.io/solomon2773/nora-nemoclaw-agent:latest` | Container image for NemoClaw. Mutable registry tags refresh before Docker/Remote Docker create; normal local setup/deploy rebuilds Nora's exact local tag, while custom refs and remote/Kubernetes preloads remain operator-owned. |

## OpenClaw runtime

These variables tune the OpenClaw runtime image and gateway. Most operators leave them at their defaults; override when running a forked OpenClaw build or pinning to a specific image.

| Variable                   | Required | Default                  | Description                                                                                |
| -------------------------- | -------- | ------------------------ | ------------------------------------------------------------------------------------------ |
| `OPENCLAW_DOCKER_IMAGE`    | No       | OpenClaw default         | Container image used for OpenClaw agents.                                                  |
| `OPENCLAW_STANDARD_IMAGE`  | No       | OpenClaw default         | Image used specifically for the `standard` sandbox profile.                                |
| `OPENCLAW_GATEWAY_PORT`    | No       | `18789`                  | Internal port the OpenClaw gateway listens on inside the agent container.                  |
| `OPENCLAW_GATEWAY_TOKEN`   | No       | auto-generated per agent | Static gateway token override. Most deployments rely on the per-agent token Nora issues.   |
| `OPENCLAW_DOCKER_PACKAGE`  | No       | `openclaw@2026.6.11`     | Validated package spec used across Docker, Kubernetes, and as the Proxmox fallback.        |
| `OPENCLAW_TSX_BIN`         | No       | OpenClaw default         | Path to the `tsx` runner inside the OpenClaw image.                                        |
| `OPENCLAW_TSX_PACKAGE`     | No       | OpenClaw default         | Override `tsx` package name.                                                               |
| `OPENCLAW_CLI_PATH`        | No       | OpenClaw default         | Path to the OpenClaw CLI inside the image.                                                 |
| `OPENCLAW_DISABLE_BONJOUR` | No       | `true`                   | Disable Bonjour/mDNS inside managed containers; set `0` only for deliberate LAN discovery. |

When `OPENCLAW_DOCKER_PACKAGE` is overridden for NemoClaw, `NEMOCLAW_SANDBOX_IMAGE` must reference a
prebuilt image containing the same exact OpenClaw version. The non-root NemoClaw runtime fails
closed on a version mismatch instead of replacing the global package inside the sandbox.

## Hermes runtime

| Variable              | Required | Default        | Description                                                                            |
| --------------------- | -------- | -------------- | -------------------------------------------------------------------------------------- |
| `HERMES_DOCKER_IMAGE` | No       | Hermes default | Container image used for Hermes agents when `hermes` is in `ENABLED_RUNTIME_FAMILIES`. |

## Agent Hub source catalog

Configure where Agent Hub fetches community templates from. Internal templates are seeded automatically.

| Variable                 | Required | Default                        | Description                                                                                                 |
| ------------------------ | -------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `NORA_AGENT_HUB_URL`     | No       | `https://nora.solomontsao.com` | Base URL of the Agent Hub source catalog.                                                                   |
| `NORA_AGENT_HUB_API_KEY` | No       | —                              | API key for the source catalog. Stored encrypted in Admin Settings; the env value bootstraps the first run. |

## Workspace invitations

| Variable                       | Required | Default | Description                                                                             |
| ------------------------------ | -------- | ------- | --------------------------------------------------------------------------------------- |
| `NORA_WORKSPACE_INVITE_SECRET` | No       | —       | Secret used to sign workspace invitation tokens. Falls back to `JWT_SECRET` when unset. |

## Integrations

| Variable                   | Required | Default                         | Description                                                                             |
| -------------------------- | -------- | ------------------------------- | --------------------------------------------------------------------------------------- |
| `NORA_INTEGRATIONS_DIR`    | No       | `backend-api/integrations-data` | Directory where integration runtime files are written before being mounted into agents. |
| `NORA_INTEGRATIONS_CONFIG` | No       | —                               | Path to a JSON file overriding the default integration catalog.                         |

## Managed backups

`NORA_BACKUP_STORAGE` selects the destination. Supported values: `local` (default volume), `s3`, `r2`, `ssh`. Leave the destination credentials empty to manage them through Admin Settings instead of `.env`.

| Variable                      | Required | Default | Description                                                                          |
| ----------------------------- | -------- | ------- | ------------------------------------------------------------------------------------ |
| `NORA_BACKUP_STORAGE`         | No       | `local` | Destination type. One of `local`, `s3`, `r2`, `ssh`.                                 |
| `NORA_BACKUP_DIR`             | No       | —       | Override path for `local` destination. Defaults to the `nora_backups` Docker volume. |
| `NORA_BACKUP_LIMIT_PER_AGENT` | No       | `10`    | Maximum number of retained backups per agent.                                        |
| `NORA_BACKUP_STORAGE_MB`      | No       | `51200` | Soft cap on total backup storage in megabytes.                                       |
| `NORA_BACKUP_RETENTION_DAYS`  | No       | `30`    | Number of days to retain expired backups before pruning.                             |

### S3-compatible

Use `NORA_BACKUP_STORAGE=s3` for AWS S3 or any S3-compatible service. Set `NORA_BACKUP_S3_ENDPOINT` for non-AWS providers.

| Variable                           | Required | Default | Description                             |
| ---------------------------------- | -------- | ------- | --------------------------------------- |
| `NORA_BACKUP_S3_BUCKET`            | No       | —       | S3 bucket name.                         |
| `NORA_BACKUP_S3_REGION`            | No       | —       | AWS region or compatible region string. |
| `NORA_BACKUP_S3_ENDPOINT`          | No       | —       | Custom endpoint for non-AWS providers.  |
| `NORA_BACKUP_S3_ACCESS_KEY_ID`     | No       | —       | Access key.                             |
| `NORA_BACKUP_S3_SECRET_ACCESS_KEY` | No       | —       | Secret access key.                      |
| `NORA_BACKUP_S3_SESSION_TOKEN`     | No       | —       | Optional STS session token.             |

### Cloudflare R2

Use `NORA_BACKUP_STORAGE=r2`. Set `NORA_BACKUP_R2_ENDPOINT` to your account endpoint, e.g. `https://<account-id>.r2.cloudflarestorage.com`.

| Variable                           | Required | Default | Description                       |
| ---------------------------------- | -------- | ------- | --------------------------------- |
| `NORA_BACKUP_R2_BUCKET`            | No       | —       | R2 bucket name.                   |
| `NORA_BACKUP_R2_REGION`            | No       | —       | Region.                           |
| `NORA_BACKUP_R2_ENDPOINT`          | No       | —       | Account-specific R2 endpoint URL. |
| `NORA_BACKUP_R2_ACCESS_KEY_ID`     | No       | —       | R2 access key.                    |
| `NORA_BACKUP_R2_SECRET_ACCESS_KEY` | No       | —       | R2 secret access key.             |
| `NORA_BACKUP_R2_SESSION_TOKEN`     | No       | —       | Optional session token.           |

### SSH / SFTP

Use `NORA_BACKUP_STORAGE=ssh`.

| Variable                      | Required | Default | Description                                        |
| ----------------------------- | -------- | ------- | -------------------------------------------------- |
| `NORA_BACKUP_SSH_HOST`        | No       | —       | SSH host.                                          |
| `NORA_BACKUP_SSH_PORT`        | No       | `22`    | SSH port.                                          |
| `NORA_BACKUP_SSH_USERNAME`    | No       | —       | SSH user.                                          |
| `NORA_BACKUP_SSH_REMOTE_PATH` | No       | —       | Remote path where backup archives are uploaded.    |
| `NORA_BACKUP_SSH_PRIVATE_KEY` | No       | —       | PEM-encoded private key for SSH auth.              |
| `NORA_BACKUP_SSH_PASSWORD`    | No       | —       | Password fallback when key auth is not configured. |

## Security

| Variable       | Required | Default                 | Description                                                                                                                                         |
| -------------- | -------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CORS_ORIGINS` | No       | `http://localhost:8080` | Comma-separated list of allowed CORS origins. Update to your public origin when Nora is exposed on a domain, for example `https://app.example.com`. |

## LLM key storage

| Variable                        | Required | Default    | Description                                                                                                                                                     |
| ------------------------------- | -------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `KEY_STORAGE`                   | No       | `database` | Where LLM provider keys are stored. `database` encrypts and persists keys in PostgreSQL. `env` reads keys from environment variables instead.                   |
| `MICROSOFT_FOUNDRY_API_KEY`     | No       | —          | Foundry API key when using `KEY_STORAGE=env` or injecting provider credentials directly into a runtime.                                                         |
| `MICROSOFT_FOUNDRY_BASE_URL`    | No       | —          | Per-resource Foundry endpoint, for example `https://<resource>.services.ai.azure.com/openai/v1/`. Required for Microsoft Foundry runtime registration.          |
| `MICROSOFT_FOUNDRY_DEPLOYMENT`  | No       | —          | Exact Azure deployment name to use as the runtime model, for example `gpt-5.5-1`. Deployment names are operator-defined and must not be shortened to `gpt-5.5`. |
| `MICROSOFT_FOUNDRY_API_VERSION` | No       | —          | Optional Foundry API version for classic Azure OpenAI endpoint shapes. Leave empty when using the OpenAI v1 GA path.                                            |

## TLS

Optional; only relevant when nginx terminates TLS directly inside the stack.

| Variable        | Required | Default | Description                                                                                   |
| --------------- | -------- | ------- | --------------------------------------------------------------------------------------------- |
| `TLS_CERT_PATH` | No       | —       | Path to the TLS certificate file on the host. Used when nginx is configured to terminate TLS. |
| `TLS_KEY_PATH`  | No       | —       | Path to the TLS private key file on the host.                                                 |

## Marketing analytics

Optional privacy-light analytics for the public marketing site, powered by [Plausible](https://plausible.io). Off by default — self-hosted deployments ship zero tracking unless `NEXT_PUBLIC_ANALYTICS_DOMAIN` is set at build time.

| Variable                       | Required | Default                             | Description                                                                          |
| ------------------------------ | -------- | ----------------------------------- | ------------------------------------------------------------------------------------ |
| `NEXT_PUBLIC_ANALYTICS_DOMAIN` | No       | —                                   | Plausible domain for the public marketing site. Empty (default) ships zero tracking. |
| `NEXT_PUBLIC_ANALYTICS_SRC`    | No       | `https://plausible.io/js/script.js` | Plausible script URL; point at a self-hosted Plausible instance if you run one.      |

## Cost reporting

These optional rates drive the Cost dashboards. Nora estimates spend from token usage recorded by OpenClaw, Hermes, and future runtime chat paths.

| Variable                | Required | Default | Description                                                                                         |
| ----------------------- | -------- | ------- | --------------------------------------------------------------------------------------------------- |
| `COST_PER_1K_TOKENS`    | No       | `0.002` | Fallback LLM token cost when a per-model rate is unavailable or historical usage has no model data. |
| `COST_MODEL_RATES_JSON` | No       | —       | JSON object mapping model ids or `provider/model` ids to token rates.                               |

Example:

```env theme={null}
COST_MODEL_RATES_JSON={"openai/gpt-5.5":{"input_per_1k":0.002,"output_per_1k":0.008},"claude-sonnet-4-5":{"input_per_1k":0.003,"output_per_1k":0.015},"flat-model":{"per_1k":0.004}}
```

Use `input_per_1k` and `output_per_1k` when Nora records prompt/completion token splits. Use `per_1k` for total-token-only pricing.

## Legacy AWS backup variables

These variables are read by older platform-settings code paths for backward compatibility. New deployments should use the `NORA_BACKUP_S3_*` family above instead.

| Variable                | Required | Default | Description                            |
| ----------------------- | -------- | ------- | -------------------------------------- |
| `AWS_S3_BUCKET`         | No       | —       | Legacy S3 bucket alias for backups.    |
| `AWS_ACCESS_KEY_ID`     | No       | —       | Legacy AWS access key ID.              |
| `AWS_SECRET_ACCESS_KEY` | No       | —       | Legacy AWS secret access key.          |
| `AWS_SESSION_TOKEN`     | No       | —       | Legacy optional AWS STS session token. |
