# Settings reference

Every setting you can give a self-hosted Docustay, as environment variables in `.env` (the compose file reads it). `./init.sh` fills in the secrets. Anything not listed in the first tables is internal or belongs to the hosted service and should stay unset.

## Secrets (init.sh makes them)

| Setting | Required | Default | What it does |
|---|---|---|---|
| `KEYSTONE_DB_PASSWORD` | yes | `(generated)` | The password of the bundled Postgres. Used by compose to build the database URL. |
| `KEYSTONE_TOKEN_PEPPER` | yes | `(generated)` | A secret mixed into every API key and session digest. Changing it signs everyone out and invalidates every API key. |
| `KEYSTONE_LOCAL_AUTH_SECRET` | yes | `(generated)` | Signs the built-in sign-in tokens. Changing it signs everyone out. |
| `KEYSTONE_SETUP_TOKEN` | first run | `(generated)` | The one-time key that lets you create the owner account at /setup. Unused after setup. |
| `KEYSTONE_MASTER_KEY` | yes (production) | `(generated)` | Wraps the keys that seal stored secrets (webhook secrets, SMTP passwords, two-step secrets). Back it up with the database: without it sealed secrets cannot be opened. |

## Where things are

| Setting | Required | Default | What it does |
|---|---|---|---|
| `KEYSTONE_PUBLIC_URL` | yes behind a domain | `http://localhost:8080` | The address people open Docustay at, without a path. Every signing link, reset link and email starts with it. |
| `KEYSTONE_DOCUSTAY_APP_URL` | no | `= KEYSTONE_PUBLIC_URL` | The address of the signed-in app, when it differs from the signing address. |
| `KEYSTONE_DOCUSTAY_SIGN_URL` | no | `= KEYSTONE_PUBLIC_URL` | The address signing links start with. |
| `DOCUSTAY_PORT` | no | `8080` | The port on this machine the web app listens on. |
| `PORT` | no | `8080` | The port the API process listens on inside its container. |
| `KEYSTONE_TRUSTED_PROXY_HOPS` | behind a proxy | `0` | How many reverse proxies sit in front. With 0 every visitor looks like the proxy and shares one rate limit; set 1 behind one proxy. |

## Database and files

| Setting | Required | Default | What it does |
|---|---|---|---|
| `KEYSTONE_DB_SUPERUSER_URL` | yes | `(compose)` | Connection string the migration runner uses. It creates the application roles on first run. |
| `KEYSTONE_DATABASE_URL` | no | `(derived)` | Connection string for the application role, when you run the database yourself. |
| `KEYSTONE_DB_SSL` | no | `on` | Set to off for a database in the same private network without TLS (the bundled compose does). |
| `KEYSTONE_PG_POOL_MAX` | no | `10` | Largest number of database connections the API keeps. |
| `KEYSTONE_S3_ENDPOINT` | yes | `(compose)` | Address of the S3-compatible store that keeps signed PDFs. |
| `KEYSTONE_S3_BUCKET` | yes | `docustay` | The bucket (created for you by the bundled store). |
| `KEYSTONE_S3_ACCESS_KEY_ID` | yes | `(generated)` | Access key for the file store. |
| `KEYSTONE_S3_SECRET_ACCESS_KEY` | yes | `(generated)` | Secret key for the file store. |
| `KEYSTONE_S3_REGION` | no | `us-east-1` | Region name some S3 providers require. |
| `KEYSTONE_GOTENBERG_URL` | yes | `(compose)` | Address of the PDF converter (Gotenberg) that prints documents. |

## Email

| Setting | Required | Default | What it does |
|---|---|---|---|
| `RESEND_API_KEY` | one of Resend or SMTP |  | Your own Resend API key. Leave empty to use your own SMTP server (Settings → Email). |
| `KEYSTONE_MAIL_SENDER` | no |  | The From address used until you verify your own domain. |
| `KEYSTONE_MAIL_DAILY_LIMIT` | no | `(none)` | Hard cap on emails sent per day by this install. |
| `KEYSTONE_EMAIL_ALLOWLIST` | no | `(off)` | Comma list of addresses/domains mail may go to. Everything else is dropped. Use it on a test install. |

## Sign-in

| Setting | Required | Default | What it does |
|---|---|---|---|
| `KEYSTONE_LOCAL_AUTH` | yes (self-host) | `1` | Uses the built-in sign-in (email and password). Firebase is for the hosted service only. |
| `KEYSTONE_PASSKEYS` | no | `off` | Set to 1 to let people sign in with passkeys. Needs KEYSTONE_PASSKEY_ORIGINS. |
| `KEYSTONE_PASSKEY_ORIGINS` | with passkeys | `= KEYSTONE_DOCUSTAY_APP_URL` | Comma list of web addresses a passkey may be used from. A passkey is tied to the address's host name. |
| `DOCUSTAY_OIDC_ISSUER` | no | `(off)` | The address of your OpenID Connect provider (Keycloak, Authentik, Entra, Okta…), for example https://sso.example.com/realms/main. Setting this, the client id and the client secret turns on single sign-on. |
| `DOCUSTAY_OIDC_CLIENT_ID` | no | `(none)` | The client id you made for Docustay at your provider. |
| `DOCUSTAY_OIDC_CLIENT_SECRET` | no | `(none)` | The client secret for that client. Keep it as private as the other secrets in `.env`. |
| `DOCUSTAY_OIDC_LABEL` | no | `your identity provider` | The name on the sign-in button: “Sign in with …”. |
| `DOCUSTAY_OIDC_SCOPES` | no | `openid email profile` | What to ask the provider for. It must include `openid` and `email`. |

## Signing certificate and timestamps

| Setting | Required | Default | What it does |
|---|---|---|---|
| `KEYSTONE_SIGNING_CERT_PEM` | no | `(made at first run)` | The certificate that seals finished documents. By default Docustay makes one for this install; set your own to use a certificate from a trusted authority. |
| `KEYSTONE_SIGNING_CERT_PATH` | no |  | Path to a PEM file instead of the value itself. |
| `DOCUMENT_TSA_URL` | no | `(none)` | A trusted timestamp authority; when set, finished documents carry a trusted timestamp. |

## Operations

| Setting | Required | Default | What it does |
|---|---|---|---|
| `KEYSTONE_METRICS_TOKEN` | no | `(metrics off)` | Turns on GET /metrics (Prometheus). Callers send Authorization: Bearer <this>. At least 16 characters. |
| `DOCUSTAY_TELEMETRY` | no | `on` | off stops the one anonymous daily usage ping (a random install id, the version, five counts). |
| `DOCUSTAY_LICENSE_KEY` | no |  | A Pro licence key. With it the worker checks the licence daily and unlocks Pro features. |
| `KEYSTONE_ENVIRONMENT` | no | `production` | staging for a test install: live Stripe keys are refused and email goes only to the allowlist. |
| `KEYSTONE_ALERT_EMAIL` | no |  | Where the install sends operational alerts (a failing backup, a paused sender). |
| `KEYSTONE_SELF_HOST` | set by compose | `1` | Marks this as a self-hosted install: hides hosted-only screens such as billing. |

## Payments (optional)

| Setting | Required | Default | What it does |
|---|---|---|---|
| `KEYSTONE_STRIPE_SECRET_KEY` | for payment fields |  | Your own Stripe secret key for collecting payments at signing. |
| `KEYSTONE_STRIPE_PUBLISHABLE_KEY` | for payment fields |  | The matching publishable key. |
| `KEYSTONE_STRIPE_CHARGES_ENABLED` | no | `off` | A safety switch: live charges stay refused until this is on. |

## AI (optional)

| Setting | Required | Default | What it does |
|---|---|---|---|
| `ANTHROPIC_API_KEY` | for AI features |  | Your own Anthropic key. Without it the AI drafting, reviewing and chat features are hidden. |
| `KEYSTONE_AI_DEFAULT_MODEL` | no | `(a current model)` | The model used for drafting. |
| `KEYSTONE_AI_REVIEW_MODEL` | no | `(a current model)` | The model used for review passes. |

## Advanced settings

The code also reads the variables below. They tune limits and connections you will rarely need to change. Another 70 variables (plan prices and limits, and the hosted service's own accounts for Stripe Connect, Firebase, Cloudflare, domains and the like) belong to the hosted service only; a self-hosted install ignores them.

- `DOCUMENT_SIGNING_CERT_P12`
- `DOCUMENT_SIGNING_CERT_PASSPHRASE`
- `DOCUSTAY_API_KEY`
- `FAULT_INJECT`
- `GOOGLE_CALENDAR_API_BASE`
- `GOOGLE_OAUTH_CLIENT_ID`
- `GOOGLE_OAUTH_CLIENT_SECRET`
- `GOOGLE_TOKEN_URL`
- `KEYSTONE_APP_URL`
- `KEYSTONE_DATA_DIR`
- `KEYSTONE_DOCUSTAY_MAIL_SENDER`
- `KEYSTONE_EMAIL_FROM`
- `KEYSTONE_LIMITER_POOL_MAX`
- `KEYSTONE_MAIL_ALERT_TO`
- `KEYSTONE_MAIL_CAPTURE_SMTP`
- `KEYSTONE_PRODUCT`
- `KEYSTONE_VERSION`
- `KEYSTONE_WORKER_DATABASE_URL`
- `MICROSOFT_GRAPH_OAUTH_CLIENT_ID`
- `MICROSOFT_GRAPH_OAUTH_CLIENT_SECRET`

## An example `.env`

`init.sh` writes the secrets for you. Everything else is yours to set in the same file; a small install behind its own address needs only these lines on top of the generated ones:

```bash
KEYSTONE_PUBLIC_URL=https://sign.example.com
KEYSTONE_TRUSTED_PROXY_HOPS=1
KEYSTONE_MAIL_SENDER=Example Co <documents@example.com>
KEYSTONE_MAIL_DAILY_LIMIT=500
```

Apply a change by recreating the containers, then check the install is up and sees the new address:

```bash
docker compose up -d
curl -s https://sign.example.com/readyz
```
