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

# Configuration reference

> Reference for ekso.json — the install-time configuration that ships in every bundle. Secrets and Database only; everything else lives in the admin Settings UI.

`ekso.json` is the single source of truth for install-time configuration. The bundle ships with a complete `ekso.json` pre-configured for the install path you picked — most customers never edit it. This page documents every field, what it does, and when you'd touch it.

`ekso.json` is intentionally minimal: just the cryptographic keys the app needs to *boot* before the database exists, plus the database connection string. Everything else — mail provider, storage backend, S3 credentials, SMTP password, AI keys, license activation — lives in the database and is configured via the **Settings** UI after first-run, with sensitive values encrypted at rest using the `Secrets.EncryptionKey` field below.

## File layout

`ekso.json` lives next to `docker-compose.yml` in the bundle. Compose mounts it into both the `app` and `worker` containers. Edit, then `docker compose restart` to pick up changes.

The bundle ships two sibling folders alongside `ekso.json`: `./data/` (database files and attachments) and `./backups/` (automatic daily snapshots). Those are not configured here — they're properties of the `docker-compose.yml`. See [Data management](/guide/manage/data-management) for layout, retention, and restore details.

## Sections

| Section                 | Purpose                                                                      |
| ----------------------- | ---------------------------------------------------------------------------- |
| [`Secrets`](#secrets)   | Install-lifetime cryptographic keys: JWT signing + admin-settings encryption |
| [`Database`](#database) | Postgres or SQL Server connection strings                                    |

That's it. There are no `Misc`, `Smtp`, or `Storage` sections in `ekso.json` — those settings now live in the admin **Settings** UI. See [Configure storage and email](/guide/manage/storage-and-email) for the post-install walkthrough.

## Secrets

```json theme={null}
"Secrets": {
    "JwtKey":        "<32-char random string — pre-baked per download>",
    "EncryptionKey": "<32-char random string — pre-baked per download>"
}
```

| Field                   | Description                                                                                                                                                                                                                                           |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Secrets.JwtKey`        | Symmetric secret used to sign user session JWTs. Pre-baked unique-per-download. Minimum 32 bytes of entropy.                                                                                                                                          |
| `Secrets.EncryptionKey` | AES-GCM key for encrypting admin-managed settings (S3 credentials, SMTP password, Resend API key, mailbox/ticketing creds). Pre-baked unique-per-download. Either a 32-character ASCII string OR base64-encoded 32 bytes (44 chars with `=` padding). |

<Warning>
  Rotating `Secrets.JwtKey` signs every active user out — every session token in the wild was signed with the old key and will no longer validate. Plan a rotation window and brief your users.

  Rotating `Secrets.EncryptionKey` makes every encrypted admin setting unreadable. After a rotation, re-enter S3 credentials, SMTP password, Resend API key, mailbox creds, etc. via the Settings UI. Plan accordingly.
</Warning>

<Note>
  The session-JWT issuer and audience are install-wide constants (both `"Ekso"`). They are not configurable.
</Note>

### Generating new keys

You don't normally generate these yourself — every download from ekso.app ships an `ekso.json` with both fields pre-populated, and the Render Blueprint mints them via `generateValue: true`. The recipes below are for the rare cases that genuinely need a fresh key: a planned rotation, an `ekso.local.json` for dev, or a custom orchestrator that bypasses the standard install flows.

**`Secrets.JwtKey`** — any string with at least 32 bytes of entropy. The Dotcom seeder uses 48 random bytes encoded as base64url:

```bash theme={null}
# macOS / Linux — 64-char base64url, no padding
openssl rand -base64 48 | tr '+/' '-_' | tr -d '='
```

```powershell theme={null}
# Windows PowerShell — same shape
[Convert]::ToBase64String((1..48 | ForEach-Object { Get-Random -Maximum 256 })).TrimEnd('=').Replace('+','-').Replace('/','_')
```

**`Secrets.EncryptionKey`** — 32 random bytes, base64-encoded. The cipher accepts either form (32-char ASCII or base64-of-32-bytes); the base64 form is canonical and avoids the "did you mean 32 chars or 32 bytes" trap:

```bash theme={null}
# macOS / Linux — 44-char base64 with one `=` padding char
openssl rand -base64 32
```

```powershell theme={null}
# Windows PowerShell
[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Maximum 256 }))
```

```python theme={null}
# Cross-platform Python
import secrets, base64
print(base64.b64encode(secrets.token_bytes(32)).decode())
```

The `EksoCipher` constructor refuses to boot on a missing or wrong-length `EncryptionKey` with a clear error — your Backend logs will tell you immediately if you've pasted the wrong value.

## Database

```json theme={null}
"Database": {
    "Provider": "Postgres",
    "TransactConnection": "Host=...",
    "MartConnection": "Host=..."
}
```

| Field                         | Description                                                                                                                                                                                     |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Database.Provider`           | `"Postgres"` (recommended) or `"SqlServer"`. Determines which dialect, driver, and Dapper type-handlers Ekso wires up against the connection strings below.                                     |
| `Database.TransactConnection` | OLTP connection string — the live-work database. pgvector required for [AI features](/guide/concepts/ai) when using Postgres; SQL Server 2025 required for vector search when using SQL Server. |
| `Database.MartConnection`     | Reporting/analytics connection. Can point to the same instance as Transact for small installs; separate it onto a read replica for heavy reporting.                                             |

<Note>
  Connection strings use the dialect implied by `Provider`. Use Postgres key=value syntax (`Host=...;Port=...;Database=...`) when `Provider = "Postgres"`; use SQL Server syntax (`Server=...;Database=...;User Id=...`) when `Provider = "SqlServer"`.

  Query timeout (300s), max pool size (100), and Postgres auto-prepare (off, PgBouncer-safe) are hardcoded. They were never customised per install, so they're no longer config knobs.
</Note>

<Warning>
  Ekso emits the schema for whichever `Provider` you pick on first boot. Don't switch providers on a populated install — back up, point at a fresh database, restore.
</Warning>

## Override mechanisms

Outside Docker (Backend dev, custom orchestrators), .NET configuration binding accepts these alternatives, in increasing precedence:

1. **`ekso.json`** — committed default
2. **`ekso.local.json`** — same directory, gitignored, for developer overrides
3. **Environment variables** — section separator is `__`, e.g. `Secrets__JwtKey`, `Database__TransactConnection`

The last value wins. Env vars are useful for dev shells and CI runners; `ekso.local.json` for persistent dev overrides; `ekso.json` for the canonical install config. The Render Blueprint sets every field via env vars (Secrets via `generateValue: true`, Database via `fromDatabase`) — there's no `ekso.json` on Render at all.

## Settings *not* in ekso.json

Everything operational lives in the database and is configured via the **Settings** UI after first run, with sensitive values encrypted at rest using `Secrets.EncryptionKey`:

* **Outbound mail** — Resend or SMTP credentials. See [Configure storage and email](/guide/manage/storage-and-email) for the walkthrough.
* **Storage backend** — Local filesystem or S3-compatible bucket (AWS S3, Cloudflare R2, Backblaze B2, MinIO). Same guide.
* **AI providers and keys** — OpenAI, Azure OpenAI, Anthropic. Per-tenant. See [Settings → AI](/guide/concepts/ai).
* **License activation** — pasted from your welcome email during the [first-run wizard](/guide/install/first-run); rotates with the licence.
* **Authentication providers** (Entra SSO, password policies, 2FA) — see [Authentication](/guide/concepts/authentication).
* **Mailbox / ticketing config**, **rule webhooks**, **integrations** — see the per-feature pages under Core concepts.

This split is deliberate: install-time config is the minimum to *boot* before the database exists. Everything else lives in the database, edited via the UI, scoped per tenant where applicable.
