Configuration¶
For operators
Every environment variable the chat understands, by feature. The repository's .env is the defaults file; a test fails if it lists a variable the code does not read, or the code reads one it does not list.
Cerea reads its settings from the environment; the repository's .env lists
every variable the app understands, with its comments and its default. A value
exported into the environment, or set in .env.local, wins over .env.
In a deployment you rarely set these by hand: the deploy kit's .env.example is
the deployment's own commented list, and ./configure --set KEY=VALUE changes a
single one (see Deploying). Switches take the string true or
false; the defaults below say which way an unset one goes.
The variables that choose your deployment shape¶
Three variables decide what kind of deployment you are running, before any of the fork's variables below matter:
| Variable | What it decides |
|---|---|
OPENAI_BASE_URL |
Where inference goes: a Pystino gateway's /v1, or any OpenAI-compatible endpoint. No default: empty, the chat refuses to start. The kit sets it to the bundled gateway (http://gateway:8000/v1, or CHAT_OPENAI_BASE_URL). |
USE_USER_TOKEN |
Names the mode (below). true is Pystino mode: users sign in through the gateway and every inference call carries their own token, so quotas and the ledger are theirs. Unset or false is standalone mode: one shared OPENAI_API_KEY and no per-user billing. A hand-built chat pointed at Pystino without true silently bills everything to the shared key. |
OPENID_PROVIDER_URL |
The identity provider users sign in with. No default: empty, sign-in has no issuer to talk to (set it with OPENID_CLIENT_ID); the kit sets it from OIDC_ISSUER. The computer agent's issuer (CODE_MACHINE_ISSUER) defaults to it. |
Two modes, never a mix¶
The chat reaches models in exactly one of two ways, decided by USE_USER_TOKEN
(no other setting names the mode):
Pystino mode (USE_USER_TOKEN=true) |
Standalone mode (unset or not true) |
|
|---|---|---|
| Credential | each user's own sign-in token, only | one shared OPENAI_API_KEY, only |
| Endpoints | the gateway at OPENAI_BASE_URL, for every model, including MODELS overrides |
one OpenAI-compatible endpoint |
OPENAI_API_KEY |
must be empty | the shared key |
| Boot catalogue | fetched with no key, then filtered per user and billing group | fetched with the shared key |
A mix either bypasses the gateway's per-user access, billing and redaction, or hides models users are entitled to, so the chat refuses to start on one, and the error names the setting to change:
OPENAI_API_KEYset whileUSE_USER_TOKEN=true: empty it, or setUSE_USER_TOKEN=falsefor a standalone endpoint.- In Pystino mode, a
MODELSentry whoseendpointsname abaseURLother thanOPENAI_BASE_URL(an endpoint with nobaseURLcounts: it defaults toapi.openai.com), or carry their ownapiKey: removeendpointsfrom that entry. Overrides that only set flags such assupportsArtifactsare fine. USE_USER_TOKEN=truewith noOPENAI_BASE_URL: there is no gateway to present tokens to.
An install from before this rule that mixed the two fails to start after
upgrading until one of those is changed. With the deploy kit, run
./configure: it reports the mix and offers the fix. It can only write one
mode: the generic preset writes a shared key with personal tokens off, every
other preset writes no key.
The Deploying page shows the two complete variable sets, for a chat against Pystino and for a chat against any other endpoint.
Sign-in¶
| Variable | What |
|---|---|
OPENID_INTERNAL_URL |
The server-to-server base for the issuer, when OPENID_PROVIDER_URL would hairpin through the public proxy (the bundled Authelia: http://authelia:9091/authelia). Browsers still use OPENID_PROVIDER_URL |
OPENID_SCOPES |
The scopes the chat asks the identity provider for, separated by spaces. The default is upstream's (HuggingChat's); the deploy kit writes openid profile email groups, less any scope the issuer's discovery document does not list, for an IdP that answers invalid_scope to groups. Keep it equal to the gateway's GATEWAY_OIDC__SCOPES. The chat adds offline_access itself when the issuer lists it, to get the refresh token scheduled chats run on: the IdP's chat client must allow refresh tokens and offline_access (the bundled Authelia client does) |
OPENID_OFFLINE_ACCESS |
off stops the sign-in asking for the offline_access scope, which is what gets the refresh token behind scheduled chats and background token refresh. Anything else (default auto) asks when the issuer lists it. The kit passes it as OIDC_CHAT_OFFLINE_ACCESS |
OPENID_LOGOUT_URL |
The provider's logout page, for one that publishes no end_session_endpoint (the bundled Authelia: <issuer>/logout?rd={redirect}). {redirect} is where to land. Used before discovery when set |
ADMIN_CLI_LOGIN |
Default false. true prints a one-time admin login URL (with a token) in the server log at boot and lets a signed-in user who presents it become chat admin. It is always off when OpenID is configured, whatever this says; the kit sets it false. Admin rights come from the identity provider |
LOGOUT_ALSO_CLEAR_COOKIES |
Other applications' session cookies on this origin to expire when someone signs out, name or name:path, comma-separated. On the Pystino stack: gw_session,gw_idt:/auth, so signing out of one never leaves the other in |
CHAT_MIGRATE_ISSUER_FROM |
When moving to a new identity provider: the old issuer. A first login that finds no account takes over the old account with the same verified email |
PUBLIC_TURIN_PHRASES |
Rotating Turin-dialect greetings printed beside the logo on a new chat, one per page load, comma-separated. false or empty restores the plain name |
Secrets shared with the gateway¶
| Variable | What |
|---|---|
CHAT_SECRET_KEY |
Encrypts connector credentials at rest (AES-256-GCM). There is no default: without it connectors stop working, and nothing stores a plaintext token. Rotate it and stored connector credentials no longer decrypt |
CHAT_ERASURE_TOKEN |
The gateway's own credential for the chat's /internal/erasure (deleting a user everywhere). Minted by ./configure, sent over the compose network only, compared in constant time. The chat also refuses any /internal/* request that carries a proxy header |
Notifications¶
| Variable | What |
|---|---|
CHAT_VAPID_PUBLIC_KEY |
The public half of the deployment's Web Push key pair (base64url, 65 bytes). Minted once by ./configure. Browsers subscribe against it, so replacing it strands every subscription. Unset means push is off (Notifications) |
CHAT_VAPID_PRIVATE_KEY |
The private half (base64url, 32 bytes). Signs each push. Keep it with the other secrets in .env; a pair whose halves disagree is treated as unset, and ./configure --check says so |
CHAT_VAPID_SUBJECT |
How a push service can reach the operator: a mailto: or https: address. ./configure writes mailto: your admin email |
Documents and knowledge¶
| Variable | What |
|---|---|
CHAT_PG_URL |
PostgreSQL with pgvector, where knowledge-base passages and vectors live. Without it the knowledge store is unavailable |
CHAT_KNOWLEDGE_ENABLED |
Unset means on. false, for a deployment without the chat's Postgres, hides the Knowledge tab and the project and composer knowledge affordances |
CHAT_OCR_MODEL |
The model POST /v1/ocr uses to read an attached PDF. Office files always go to the deployment's local reader instead, whatever is named here (Knowledge). Unset: the Knowledge screen's choice, else the first kind: ocr model the caller may use. Naming an upstream model here does send documents to it, so it is a deliberate setting |
CHAT_OCR_BASE_URL |
For a deployment with no gateway: read documents by calling {base}/ocr directly (Pystino, Mistral and Cortecs serve one shape). It overrides the gateway path, reads PDFs only, caps a document at 10 MB, and requires CHAT_OCR_MODEL |
CHAT_PDF_IMAGE_PAGES |
How many pages of a scanned PDF the deployment's own reader may hand back as images, for a model that sees images (Attachments). Default 20, at most 50; 0 turns it off, and the reader then refuses a scan as before. Asked for only when the local reader is the one reading PDFs |
CHAT_OCR_TIMEOUT_MS |
How long one attached document may take to read, in milliseconds. Default 180000 (3 minutes); a 50-page PDF takes the local reader 20-30 s. When it fires the server logs document_extraction_timeout with the elapsed time and the file is stored without text. Pystino has a matching GATEWAY_EXTRACTOR__TIMEOUT_SECONDS (default 180): raise both together |
CHAT_OCR_API_KEY |
Bearer key for CHAT_OCR_BASE_URL: one shared deployment key, not the signed-in user's credential |
Features¶
| Variable | What |
|---|---|
CHAT_MEMORY_ENABLED |
Unset means on. false withdraws memory from the deployment: the tab hides, its routes answer 404, remember and forget are never offered, and a project's shared memory go the same way. Each user's own opt-in is separate and defaults off |
CHAT_ASK_USER_QUESTION_ENABLED |
Unset means on. false removes the ask_user_question tool (a question with clickable options the turn waits on) from ordinary chats |
CHAT_SKILLS_DISABLED |
Comma-separated skill names to switch off deployment-wide, whatever their source |
CHAT_CODE_TOOL_ENABLED |
true offers the execute_code tool to tool-capable models: the model's code runs in the signed-in user's own browser sandbox. Unset: the assistant's Python runs only as blocks in a finished answer; true also lets it run code mid-answer and use the result |
CHAT_CODE_EXECUTION |
Where code runs, deployment-wide and admin-only (no user or chat setting changes it): browser (the default) runs it in the signed-in user's own browser sandbox; off switches every code feature off (no execute_code, no auto-run code blocks, no Run button, no execution wording in the prompt); server is reserved for a future server-side sandbox and, today, logs a warning at boot and behaves as off. CHAT_CODE_TOOL_ENABLED keeps its meaning inside browser: unset leaves code blocks only, true also offers execute_code; off wins over it |
CODE_PAUSED_WAIT_MS |
How long a turn waiting on execute_code keeps waiting after the user's page goes to the background (a phone locking its screen), counted from the first such signal. Default 900000 (15 minutes), held between 120000 and 3600000. On return the page restarts the run and the turn continues; if the page does not return in time the model falls back to a code block |
CHAT_CODE_RUN_TIMEOUT_SECONDS |
Seconds one run of the browser sandbox may take before it is stopped and the sandbox restarts. Default 60; values are held between 5 and 300, so a runaway loop always ends. Also the kit/.env.example key |
CHAT_STREAM_IDLE_TIMEOUT_MS |
How long a model's answer may go without sending anything before the turn is stopped with "The model stopped responding". Default 300000 (5 minutes), at least 30000; the wait before the first chunk counts, so slow reasoning models need the room |
CHAT_TURN_MAX_MINUTES |
The longest one turn may run, whatever it is doing. Default 30, at least 1. Past it the turn ends as an error ("This answer took longer than N minutes and was stopped."), keeping what was streamed. A turn parked on an approval or a question ends there, and its resume gets a fresh allowance |
CHAT_PYODIDE_PYPI_DISABLED |
true forces off the sandbox's "install packages from PyPI" setting for everyone and hides the user's option. Without it the setting is on by default, so code may fetch packages from the public PyPI index (each user can turn it off). The vendored wheels stay installable |
CHAT_DICTATION |
How the composer does dictation: browser (the browser's speech recognition), model (record, then transcribe with the transcription model through the gateway) or off. The old yes, no, fallback and unset keep exactly their old behaviour, which depends on the deprecated TRANSCRIPTION_* pair (table). Settable from Administration → Assistant → Voice, which overrides this |
CHAT_TRANSCRIPTION_MODEL |
The model that transcribes attached audio and dictation through the gateway's /v1/audio/transcriptions. Unset: the first kind: transcription model the user may use. The choice on Administration → Assistant → Voice & audio wins over this value. See Transcription |
CHAT_IMAGE_MODEL |
The model that draws images for the assistant's generate_image tool, through the gateway's /v1/images/generations as the user. Unset: the first kind: image model the user may use. The choice on Administration → Assistant → Images wins over this value. See Images |
CHAT_DELEGATION |
false removes the delegate and research sub-agent tools from every chat; anything else, or unset, leaves them on. Settable from Administration → Assistant → Sub-agents, which overrides this |
ADMIN_INSTRUCTIONS |
The instructions added to every chat, up to 4,000 characters. Set under Administration; with ENABLE_CONFIG_MANAGER off, set here. |
ADMIN_INSTRUCTIONS_VISIBLE |
false hides them from users in Settings; they still apply. Anything else shows them. |
STARTER_SUGGESTIONS |
true shows four starter suggestions under the greeting on a new chat. Off by default; Administration → Assistant sets it. |
CHAT_FILE_RETENTION_DAYS |
How many days files a run produced are kept: a whole number, 0 for no expiry, default 30. Settable from Administration → Storage, which overrides this |
DELIVERABLE_SWEEP_INTERVAL_MS |
How often to sweep files a run produced for that retention, and free orphaned bytes. Default 3600000 (one hour) |
CHAT_WEBFETCH_CACHE_DAYS |
How many days the text web_fetch read from a page or document is kept for its chat, so a later turn does not download and extract (and pay for) it again: a whole number, 0 turns the cache off, default 7. Deleted with the chat and on account erasure |
CHAT_WEBFETCH_FRESH_MINUTES |
Within this many minutes of the last download a cached document is reused without downloading it again; after that it is downloaded and re-extracted only if its content changed. Default 10 |
CHAT_USAGE_ENABLED |
false hides Settings → Usage and the quota line in the user menu. Anything else shows them wherever a gateway (OPENAI_BASE_URL) is configured: your quotas and spend, read from the gateway |
CHAT_CONSOLE_ENABLED |
true when this origin serves the Pystino console at /console, so the admin panel links to it. Leave empty for a satellite or generic deployment. Links are hidden per user when their Pystino groups lack the console capability |
File storage¶
Where the chat keeps files: File storage explains the backends, bring-your-own S3 examples and the migration.
| Variable | What |
|---|---|
CHAT_BLOB_STORE |
gridfs (default: files inside MongoDB) or s3 (an S3-compatible bucket). Do not change it on a deployment that holds files without running the migration first |
CHAT_S3_ENDPOINT |
The S3-compatible store's URL. Empty means AWS S3 |
CHAT_S3_REGION |
The region; required on AWS, us-east-1 by default elsewhere |
CHAT_S3_BUCKET |
The bucket (required with s3) |
CHAT_S3_ACCESS_KEY_ID, CHAT_S3_SECRET_ACCESS_KEY |
The keys, both or neither; neither uses the standard AWS credential chain |
CHAT_S3_FORCE_PATH_STYLE |
true for host/bucket/key addressing (default with an endpoint), false for bucket.host (default on AWS) |
CHAT_S3_PREFIX |
A key prefix, to share a bucket between deployments |
CHAT_S3_SSE, CHAT_S3_SSE_KMS_KEY_ID |
Server-side encryption: AES256 or aws:kms with a key |
CHAT_S3_CREATE_BUCKET |
true creates a missing bucket at start |
Web fetch¶
| Variable | What |
|---|---|
FETCH_BACKEND |
Who reads a page the assistant fetches: direct (a plain HTTPS request; the default) or playwright (the headless browser). A third name, pystino, is recognised but not implemented and fails with a message |
CHAT_FETCH_ALLOW_INTERNAL_NETWORKS |
Set to true only if this deployment must fetch content from hosts on an internal network. By default the assistant's web fetch and "Attach from URL" refuse 10.0.0.0/8 and 100.64.0.0/10 (the range mesh VPNs such as Tailscale and NetBird use), so a model or a page cannot point them at internal services. Loopback, link-local and the other private ranges are refused regardless. MCP connectors may reach both networks. |
WEB_SEARCH_MODEL |
The search backend (a gateway kind: search model) the Web search pill uses. Overrides Administration → Web search and is shown there as "set in the environment". Unset: the admin screen's choice, else the caller's billing-group search policy. A caller not granted the named backend falls back to that policy |
PLAYWRIGHT_WS_ENDPOINT |
Where the renderer listens, inside the compose network only. Never publish it |
CHAT_GOTENBERG_URL |
Where the chat reaches Gotenberg, the document converter of the kit's office profile (Office previews). Empty turns Office previews through LibreOffice and the convert_document tool off |
CHAT_DOCUMENTS_URL |
Where the chat reaches the document service (cerea-documents, the kit's documents service), which gives models doc_create, doc_create_from_template, doc_outline, doc_read and doc_edit for Word, Excel, PowerPoint and PDF files (Word, Excel, PowerPoint and PDF files). The kit sets it to http://documents:8090; empty, or no secret, turns the tools off |
CHAT_DOCUMENTS_SECRET |
The secret the chat sends in X-Cerea-Documents-Secret; the service refuses any call without it. The kit's ./configure mints it and passes the same value to both; an install from before 0.30.0 runs ./configure once to get it |
Coding agents¶
| Variable | What |
|---|---|
CODE_AGENTS_ENABLED |
Exactly true shows the Agents panel. Anything else hides it, and /code answers 404 |
LOCAL_CONNECTORS_ENABLED |
Exactly true lets users connect MCP servers that run on their own computer (Local connectors): the computer link accepts a connectors computer, the Workspace shows the Local connectors tab, and the computer's servers appear in the MCP panel. Independent of CODE_AGENTS_ENABLED. Anything else turns it off. The deploy kit sets it to true |
CODE_FILES_ENABLED |
Unset means on with the panel. false turns the read-only file explorer off for the whole deployment (each computer can also refuse it) |
CODE_FILE_WRITE_ENABLED |
Unset means on with the file explorer. false turns saving files from the /code editor off for the whole deployment. Still only live for a computer enrolled with --allow-file-write (denied by default) |
CODE_TERMINAL_ENABLED |
Exactly true allows browser terminals; off by default. Still only live for a computer whose policy allows terminals (the enroll default; --no-terminal turns them off) |
CHAT_SCHEDULES_ENABLED |
Unset means on. Exactly false is the kill switch for scheduled actions, scheduled chats (every signed-in user) and coding-agent schedules (where CODE_AGENTS_ENABLED=true) alike: nothing fires, the API answers 404, the sidebar entries go. Rows are kept |
CHAT_AUTO_PERMISSIONS |
Unset means on. Exactly false is the kill switch for agent auto-approval: no decision-model reviewer runs and the /code composer's third step reads Allow. On grants nothing by itself: the reviewer is off until an administrator chooses a model and turns it on (Agent auto-approval) |
AUTO_REVIEW_TIMEOUT_SECONDS |
How long the reviewer's decision call may take before the ask comes to the user. Default 20 |
AUTO_REVIEW_WATCHDOG_SECONDS |
How long a held "needs you" notification waits for the reviewer before it is sent anyway. Default 60; keep it above the call timeout |
CHAT_SCHEDULES_MAX_PER_USER |
How many schedules one user may have, of every kind together. Default 20 |
CODE_MACHINE_ISSUER |
The issuer a computer's token must come from. Defaults to OPENID_PROVIDER_URL |
CODE_MACHINE_AUDIENCE |
The audience a computer's token must carry (pystino-api) |
CODE_MACHINE_CLIENT_ID |
The client a computer's token must be issued to (opencode-enrollment) |
CODE_GATEWAY_ORIGIN |
The gateway origin the pairing dialog passes to galopin enroll --gateway. Unset: the browser's own origin |
GALOPIN_DIST_DIR |
Where the image keeps the galopin binaries it serves at <base>/galopin/. Default /app/galopin-dist |
The operator's view of these, and what each does to the panel, is in The
/code panel.
Images¶
The assistant's generate_image tool draws with a gateway model of
kind: image (POST /v1/images/generations), as the signed-in user: metered
per image with their token, with their model access groups and bill-to.
Which model: the administrator's choice under Administration → Assistant →
Images, else CHAT_IMAGE_MODEL, else the first kind: image model the user may
use. The tool and the composer's "Create an image" item are offered only to a
user for whom a model resolves; the catalogue is asked with a two-second
deadline and a slow or failing gateway means no tool, not a slow page. Whether
each generation asks first is the "Ask before generating images" switch on the
same screen (no environment variable). See Images.
Transcription¶
Audio is transcribed by a gateway model of kind: transcription
(POST /v1/audio/transcriptions), as the signed-in user: it is metered per
audio minute with their token, their model access groups and bill-to. The
gateway refuses files over 25 MB. Two features use it:
- Audio attachments are transcribed once, at upload, and the transcript is
kept beside the file (never re-transcribed per turn). Audio attachments may be up to
25 MB (about 25 minutes of ordinary audio); every other attachment stays at 10 MB.
The app's
BODY_SIZE_LIMITis 40 MB to carry them. - Dictation with the microphone, when Administration → Assistant → Voice & audio is set to "Transcription model".
Which model: the administrator's choice under Administration → Assistant →
Voice & audio (it works whether or not knowledge bases are enabled), else
CHAT_TRANSCRIPTION_MODEL, else the first kind: transcription model the user
may use. A choice saved on the Knowledge screen by the one release that kept it
there is still read until one is saved on the new screen. Audio is not screened
by the gateway's redaction.
Deprecated direct endpoint. TRANSCRIPTION_BASE_URL with
TRANSCRIPTION_MODEL sends dictation straight to
${TRANSCRIPTION_BASE_URL}/${TRANSCRIPTION_MODEL} with the deployment's own
key (OPENAI_API_KEY, or the user's token only when USE_USER_TOKEN is
true), not through the gateway and not metered per user. While the pair is
set it keeps serving dictation even when CHAT_TRANSCRIPTION_MODEL or a model
chosen in the admin screen exists; those apply to audio attachments, which never
use the direct endpoint. One deprecation line is logged. Remove the pair to send
dictation through the gateway as the user.
What the old CHAT_DICTATION words do (unchanged from before):
CHAT_DICTATION |
pair unset | pair set |
|---|---|---|
unset / fallback |
browser button | the recorder, no browser button |
yes |
browser button | the recorder and the browser button |
no |
nothing | the recorder (it was never switched off) |
The new values are exact: browser is the browser button only, model is the
recorder only, off is nothing, with or without the pair. The admin screen shows
an old value with the pair set as "Transcription model".