Configuration, the README env table and the features page say Settings -> Config -> Reverse proxy is where the count is checked and set, that a saved count wins over TRUSTED_PROXY_HOPS, that Cloudflare in front of your own proxy is 2, and what a too-low count does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
130 lines
8.6 KiB
Markdown
130 lines
8.6 KiB
Markdown
# Configuration
|
|
|
|
Grimoire reads its configuration from environment variables. Most settings people change day to day (registration, SMTP, retrieval thresholds, backups, forge connections) live in **Settings** in the web UI instead.
|
|
|
|
Any variable marked *secret* can also be read from a file: set `<NAME>_FILE` to the path, for example a Docker secret at `/run/secrets/grimoire_key`.
|
|
|
|
## Environment variables
|
|
|
|
### Core
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `DATABASE_URL` *(secret)* | `postgresql+asyncpg://grimoire:grimoire@localhost:5432/grimoire` | PostgreSQL connection string, using the `asyncpg` driver |
|
|
| `SECRET_KEY` *(secret)* | kept in `DATA_DIR` | Signs sessions and seals stored credentials. Unset, the instance makes its own on first start and keeps it in `DATA_DIR/secret_key`. Set it only to manage the key yourself; it wins over the kept one (see below) |
|
|
| `DATA_DIR` | `/data` | Where the instance keeps its own `SECRET_KEY`. Mount a volume here |
|
|
| `BASE_URL` | `http://localhost:5000` | Public URL, used in email links and OIDC redirects |
|
|
| `TRUSTED_PROXY_HOPS` | `1` | The starting count of reverse proxies in front of Grimoire. **Settings → Config → Reverse proxy** is where the count is checked and set, and a count saved there wins over this. The client address and the scheme are read that many entries in from the right of `X-Forwarded-For` and `X-Forwarded-Proto`; `0` ignores both headers. See [Behind a reverse proxy](#behind-a-reverse-proxy) |
|
|
| `LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING` or `ERROR` |
|
|
| `LOG_RETENTION_DAYS` | `90` | Days of app logs to keep |
|
|
| `FASTEMBED_CACHE_DIR` | `/data/fastembed-cache` | Where the embedding model is stored after its first download |
|
|
|
|
### Sign-in
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `LOCAL_AUTH_ENABLED` | `true` | `false` turns off username/password login, leaving SSO only |
|
|
| `OIDC_ISSUER` | — | OIDC issuer URL |
|
|
| `OIDC_CLIENT_ID` | — | OIDC client ID |
|
|
| `OIDC_CLIENT_SECRET` *(secret)* | — | OIDC client secret |
|
|
| `OIDC_SCOPES` | `openid profile email` | Scopes to request |
|
|
|
|
SSO turns on when the issuer, client ID and secret are all set. See [SSO / OAuth](sso-oauth.md).
|
|
|
|
### Email
|
|
|
|
Admins can set SMTP in **Settings**; these are the defaults until they do.
|
|
|
|
| Variable | Default |
|
|
|----------|---------|
|
|
| `SMTP_HOST` | — |
|
|
| `SMTP_PORT` | `587` |
|
|
| `SMTP_USERNAME` | — |
|
|
| `SMTP_PASSWORD` *(secret)* | — |
|
|
| `SMTP_FROM_ADDRESS` | — |
|
|
| `SMTP_FROM_NAME` | `Fabled Grimoire` |
|
|
| `SMTP_USE_TLS` | `true` |
|
|
|
|
### Integrations
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `PLUGIN_MARKETPLACE_URL` | this repo's git URL | The git URL shown in the plugin install command under Settings → MCP Access. An admin can override it in Settings |
|
|
| `FORGE_KIND`, `FORGE_BASE_URL`, `FORGE_TOKEN` *(secret)* | — | A git forge connection for admins' projects, so a token can live in a Docker secret. Each user can add their own under Settings → Integrations; a stored connection for the same host wins |
|
|
| `FORGE_WEBHOOK_SECRET` *(secret)* | — | Verifies push webhooks from the forge |
|
|
| `SEARXNG_URL` | — | A SearXNG instance, listed as the web-search backend under Settings → Integrations |
|
|
|
|
## Running it
|
|
|
|
### Docker Compose
|
|
|
|
[`docker-compose.yml`](../docker-compose.yml) runs the published image with its own database, and needs nothing set:
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
Open `http://localhost:5000`. The first account registered becomes the admin; after that, registration stays closed until an admin opens it in Settings or sends an invite.
|
|
|
|
Update with `docker compose pull && docker compose up -d`. The key and the data stay in their volumes.
|
|
|
|
### From source
|
|
|
|
```bash
|
|
docker compose -f docker-compose.dev.yml up --build
|
|
```
|
|
|
|
### Docker Swarm
|
|
|
|
[`docker-compose.prod.yml`](../docker-compose.prod.yml) is a stack file: the app and database on a private overlay network, trusting one proxy (`TRUSTED_PROXY_HOPS: "1"`). A Swarm can move the app to another node, away from a node-local `/data` volume, so the stack sets its own key: provide `DB_PASSWORD` and `SECRET_KEY` in the environment (or mount a Docker secret and set `SECRET_KEY_FILE`):
|
|
|
|
```bash
|
|
DB_PASSWORD=... SECRET_KEY=... docker stack deploy -c docker-compose.prod.yml grimoire
|
|
```
|
|
|
|
The database user, database and volume default to `grimoire`. An install created under the name Scribe keeps its data by setting `POSTGRES_USER`, `POSTGRES_DB` and `PGDATA_VOLUME` to the names it was created with.
|
|
|
|
The shipped files leave out healthchecks and restart policies on purpose. The app waits for its database on startup, and the orchestrator's defaults restart it if it ever exits.
|
|
|
|
### Volumes
|
|
|
|
- **Database** (`pgdata`): everything Grimoire stores.
|
|
- **App data** (`/data`): the instance's own `SECRET_KEY`, the embedding model cache and diagnostics. Losing it costs the key: Settings then asks for saved forge tokens and the backup secret again, and the model downloads again.
|
|
|
|
Back up the database with **Settings → Data → Scheduled backups** ([Backups](backups.md)). A database backup doesn't carry the key; to move sealed credentials to a new instance, copy `/data/secret_key` with it.
|
|
|
|
## Behind a reverse proxy
|
|
|
|
Grimoire doesn't terminate TLS. Put it behind Nginx, Traefik or Caddy, forward to port 5000, and keep port 5000 off the internet. Set `BASE_URL`, then set the proxy count to the number of proxies a request passes through: one proxy is `1`, the default; Cloudflare's proxied DNS in front of your own proxy is `2`.
|
|
|
|
**Settings → Config → Reverse proxy** sets the count and checks it against your own request. It shows the address Grimoire reads you as, and what each count from 0 to 3 would read. Pick the count that shows your own address. When a request comes through Cloudflare, the section names the count that reads the address Cloudflare reports, with a button that sets it. `TRUSTED_PROXY_HOPS` in the environment is only the starting value: a count saved in Settings wins, and **Use the environment's** goes back to it.
|
|
|
|
A count that is too low makes every visitor read as the proxy, so they share one sign-in rate limit and a few bad sign-ins block everyone. The "too many attempts" message says so and points here. The block on sign-ins lifts after a minute; an admin who is already signed in can fix the count at once.
|
|
|
|
```nginx
|
|
location / {
|
|
proxy_pass http://127.0.0.1:5000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
# MCP responses can stream.
|
|
proxy_buffering off;
|
|
proxy_read_timeout 600s;
|
|
}
|
|
```
|
|
|
|
Each proxy appends its entry to `X-Forwarded-For` and `X-Forwarded-Proto`, so Grimoire reads the entry that many in from the right: the one your own proxy wrote. Whatever a client puts to the left of it is ignored. A header with fewer entries than that (a LAN road with one proxy beside a Cloudflare road with two) is read at its leftmost entry, the one the first proxy wrote.
|
|
|
|
When that entry says `https`, the session cookie is marked `Secure` and the response carries `Strict-Transport-Security: max-age=31536000`. Over plain HTTP neither is sent, and Grimoire never redirects to HTTPS, so a LAN install keeps working.
|
|
|
|
A hop count higher than the real number of proxies lets a client choose its own address, so count them. With `0`, the headers are ignored and the socket peer is the client: use it when clients reach port 5000 directly.
|
|
|
|
## Security checklist
|
|
|
|
- **Keep the key.** Forge tokens and the backup destination's S3 secret are sealed under a key derived from `SECRET_KEY`, so a copy of the database alone can't read them. Unset, the instance makes a random key and keeps it in `/data/secret_key`, readable by its owner only; keep that volume. If the key changes (say, a restore onto an instance with a different key), Settings marks those credentials and asks for them again; nothing else breaks. The app warns at startup when `SECRET_KEY` is a public placeholder or `/data` is not a mounted volume, and refuses to start with a placeholder key (`dev-secret-change-me`, `change-me-in-production`).
|
|
- **The proxy count** (Settings → Config → Reverse proxy) matches the number of proxies in front of Grimoire, so rate limits and the audit log see the real client and the cookie is `Secure` over HTTPS. The section shows whether it does.
|
|
- **Registration** is open only for the first account. Invite people, or open it briefly from Settings.
|
|
- **Signing everyone else out**: changing a password ends that user's other sessions; Settings → Account has a button for it too.
|
|
- **API keys** are per user, shown once, and can be read-only. Revoke any you no longer use under Settings → MCP Access.
|