Operator Authentication
-
Quill has a single privileged role: the operator.
The Dashboard API key controls access to the dashboard and operational API. -
This article explains how the operator signs in, how programmatic callers authenticate,
and what to do when the key is lost or exposed. -
In this article:
The Dashboard API key
Quill reads its Dashboard API key from the QUILL_API_KEY environment variable when the container starts.
In the default Docker Compose configuration, you supply it in the .env file next to docker-compose.yml.
The key is required:
- The default Docker Compose configuration prevents the container from starting when
QUILL_API_KEYis missing. - If Quill is started another way without the key, it rejects all requests to protected operational API endpoints.
It does not allow anonymous access or use a default key. - When the key is configured, it can be used to create a browser session or authenticate an API request directly.
For validation, Quill stores a salted SHA-256 hash in its configuration database and compares hashes using a constant-time operation. Quill does not expose the original key through its dashboard or API.
The original value remains in your deployment configuration, such as the .env file and the container environment.
Protect access to that configuration as you would any other secret.
Do not confuse the two signup keys
The signup email contains two different credentials:
| Credential | Environment variable | Used for |
|---|---|---|
| License key | QUILL_LICENSE_KEY | Retrieving the setup package during activation |
| Dashboard API key | QUILL_API_KEY | Operator sign-in and API authentication |
Use the value assigned to QUILL_API_KEY when signing in or authenticating an API request.
Using the License key instead results in 401 Unauthorized.
If you replace the signup-issued Dashboard API key with a value of your own, use a high-entropy value.
Quill logs a startup warning when QUILL_API_KEY is shorter than 16 characters.
Signing in to the dashboard
Open https://dashboard.<domain>/ and enter the Dashboard API key on the login screen.
After validating the key, Quill issues a browser session cookie.
The browser sends this cookie with later requests, so you do not need to enter the key again during the session.
| Property | Value |
|---|---|
| Cookie name | quill.session |
| Flags | HttpOnly, Secure, SameSite=Strict |
| Authentication expiry | After 8 hours of inactivity; continued activity renews it |
| Sign out | Deletes the cookie from the current browser |
Quill validates the session cookie independently and does not re-check it against the Dashboard API key.
Signing out does not invalidate a copy of the cookie held elsewhere.
A session created before the Dashboard API key is changed therefore remains valid until it expires
or the container is recreated. See Replacing an exposed key.
The sign-in rate limit is per client IP address
-
The login endpoint accepts 10 attempts per minute from each client IP address and returns
429 Too Many Requests
when that address's limit is exceeded. -
nginx passes the original caller address to Quill, as described in Network Architecture.
Login attempts from different client IP addresses therefore use separate rate-limit buckets. -
If a client exhausts its limit, wait up to one minute before trying again from that address.
Authenticating programmatic calls
Scripts, monitoring tools, and integrations do not create browser sessions.
They send the Dashboard API key with every protected request.
The following examples both request the list of apps configured in your Quill.
They differ only in the authentication header used.
Using the X-Api-Key header:
curl -H "X-Api-Key: <dashboard-api-key>" \
https://api.<domain>/api/apps/
Using the Authorization header with the Bearer scheme:
curl -H "Authorization: Bearer <dashboard-api-key>" \
https://api.<domain>/api/apps/
Both requests return the configured apps as JSON when the key is valid.
A missing or incorrect key returns 401 Unauthorized.
api.<domain> exposes /api/* and the anonymous /healthz endpoint, but does not serve the dashboard UI.
The API endpoints are also reachable on dashboard.<domain> with the same authentication headers.
See the Quill hostnames.
One key per account
The Dashboard API key issued during signup belongs to the signup account, not to an individual Quill instance.
The same key is issued for every Quill instance registered under the same email address.
By default, you configure each of those Quill instances with the signup-issued key:
- Every Quill instance that is still configured with that key accepts it.
- If you replace
QUILL_API_KEYon a Quill instance, that instance accepts the replacement value instead.
The change does not rotate the signup-issued key or affect other Quill instances. - If the signup-issued key is exposed, treat every Quill instance that still accepts it as affected.
Replace the key and recreate the container on each affected instance.
See Replacing an exposed key.
Recovering a lost key
Quill cannot derive the original Dashboard API key from the salted hash in its configuration database,
and it does not expose the key through its dashboard or API.
First, check the deployment configuration from which QUILL_API_KEY was supplied,
such as the .env file or your secret store.
If the Quill instance still uses the signup-issued key, you can also recover it in either of these ways:
-
Check the signup email.
The email contains the Dashboard API key, the License key, and the registered domain. -
Register the same domain again.
Running the signup flow for a domain that your account already owns re-sends the same account-level
Dashboard API key and issues a fresh setup package. It does not create or rotate the key.
If the Quill instance uses a locally chosen replacement key, the signup email does not contain that value.
If you cannot recover it from your deployment configuration, configure another key and recreate the container.
See Replacing an exposed key.
Replacing an exposed key
Quill does not currently provide a built-in command for rotating or revoking the Dashboard API key.
To replace the Dashboard API key for a Quill instance, configure a new high-entropy value for QUILL_API_KEY
and recreate the container.
Docker Compose
Edit QUILL_API_KEY in the .env file next to docker-compose.yml, then run:
docker compose up -d
Compose detects the changed environment and recreates the container with the new key.
docker run
Remove the existing container, then rerun it with the new key and the same data volume:
docker rm -f quill
docker run -d --name quill \
--restart unless-stopped \
-p 443:443 \
-e QUILL_LICENSE_KEY="YOUR_EXISTING_LICENSE_KEY" \
-e QUILL_API_KEY="YOUR_NEW_DASHBOARD_API_KEY" \
-v quill-data:/var/lib/quill \
ravendb/quill:latest
If your original command used different ports, image tags, environment options, or volume names,
preserve those values when recreating the container.
Recreate the container; do not restart it
Quill reads QUILL_API_KEY from the container environment when it starts.
Restarting the existing container reuses the same environment, so docker restart quill
keeps the exposed key in force.
A browser session is not re-checked against the Dashboard API key after login.
Restarting the existing container therefore also leaves previously issued session cookies valid
until they expire after eight hours of inactivity.
With the default container layout, the cookie-protection keys are stored in the container’s writable layer, outside the quill-data volume. Recreating the container discards those keys and invalidates previously issued sessions.
Recreating the container preserves the Quill data stored in the quill-data volume,
including its databases, activated setup package, and configured apps.
Do not delete that volume or run docker compose down -v; those operations remove the persisted Quill data.
Which Quill instances must be updated depends on which key was exposed:
- If a locally chosen replacement key was exposed, replace it on that instance.
- If the signup-issued key was exposed, replace it on every Quill instance that still accepts it.
ChangingQUILL_API_KEYlocally does not rotate the account-level signup key or update other Quill instances. - Store each replacement key in your deployment secret store.
The signup email continues to contain the original signup-issued key.
What the key does not cover
The Dashboard API key, and browser sessions created from it, control access to Quill's operational API endpoints.
Three surfaces use different access controls:
-
The public chat surface
End users open embedded chat pages onpublic.<domain>without the Dashboard API key.
Each embed URL contains a bearer token whose expiration and invocation limit constrain its use.The channel's allowed origins restrict where browsers may embed the page through Content Security Policy.
They do not authenticate requests or prevent direct use of a disclosed embed token.Never place the Dashboard API key in a front-end application.
See what is public. -
Direct database access
Applications connect to RavenDB ondb.<domain>using a client certificate over mutual TLS.
The Dashboard API key cannot authenticate this connection, and a client certificate does not grant dashboard access. See direct database access. -
Health and activation endpoints
/healthzand/api/bootstrap/statusanswer without authentication,
allowing first-boot activation to be monitored before an operator can sign in.
Quill has a single operator credential
Quill does not provide separate operator accounts.
Anyone holding the Dashboard API key has full operator access to the dashboard and operational API.
Quill treats all key holders as the same operator and cannot distinguish which person performed an action.
If someone who knows the key should no longer have access, replace it on every affected Quill instance
and recreate those containers.
Summary
-
The Dashboard API key is required for Quill's operational API.
The default Docker Compose configuration prevents startup without it.
If Quill is started another way without the key, all requests to protected operational API endpoints are rejected. -
Quill stores a salted hash in its configuration database and does not expose the original key through its dashboard or API. Keep the original value in your deployment secret store.
-
Dashboard sign-in creates a sliding session cookie that expires after eight hours of inactivity.
Programmatic callers send the key with every request usingX-Api-KeyorAuthorization: Bearer. -
The login rate limit uses a separate bucket for each client IP address.
-
The signup-issued key belongs to the signup account and works on every Quill instance that is still configured with it. A locally chosen replacement applies only to the Quill instance on which it is configured.
-
Recover the signup-issued key from the signup email or by registering the same domain again.
Recover a locally chosen key from your deployment configuration, or configure another key. -
To replace an exposed key, update
QUILL_API_KEYand recreate every affected container.
Restarting an existing container does not load a changed environment or invalidate existing browser sessions. -
The Dashboard API key does not authenticate embed links, direct RavenDB connections,
/healthz,
or/api/bootstrap/status. Those surfaces use embed tokens, mTLS client certificates, or anonymous access.