Skip to content

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_KEY set while USE_USER_TOKEN=true: empty it, or set USE_USER_TOKEN=false for a standalone endpoint.
  • In Pystino mode, a MODELS entry whose endpoints name a baseURL other than OPENAI_BASE_URL (an endpoint with no baseURL counts: it defaults to api.openai.com), or carry their own apiKey: remove endpoints from that entry. Overrides that only set flags such as supportsArtifacts are fine.
  • USE_USER_TOKEN=true with no OPENAI_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.

CHAT_IMAGE_MODEL=flux-2-klein-4b

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_LIMIT is 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".