Skip to content

Environment variables

Required

Variable Notes
VENKAI_JWT_SECRET Signs dashboard sessions. Rotating it invalidates every issued token and logs every user out — treat it as permanent once a pilot has started.
POSTGRES_PASSWORD Used by the production compose for both db and api. Set once in .env.
python -c "import secrets; print(secrets.token_urlsafe(64))"   # JWT secret
python -c "import secrets; print(secrets.token_urlsafe(32))"   # Postgres password

Use at least 32 bytes for the JWT secret; PyJWT warns below that for HS256.

Both are declared with :? in the production compose, so the stack refuses to start rather than booting with an empty secret.

Core

Variable Default Notes
VENKAI_DATABASE_URL local SQLite file Any SQLAlchemy URL. Normally unset under the production compose, which composes it from POSTGRES_PASSWORD. Set it only to target an external database.
VENKAI_ENV development production | staging | development. production enables rate limiting and Secure session cookies.
VENKAI_ALLOWED_ORIGINS CORS allow-list. Only needed for clients that are not the dashboard — Caddy proxies app.venkai.fr/api/* same-origin.
VENKAI_EMBEDDING_PROVIDER hashing hashing | semantic. The Docker image sets semantic.
VENKAI_RETRIEVAL_MAX_CANDIDATES 10000 Candidates scored per query. Truncates oldest-first, with a warning.

Email Beta

Two backends. If VENKAI_SMTP_HOST is set it wins; otherwise Resend.

Variable Notes
VENKAI_SMTP_HOST e.g. mail.venkai.fr
VENKAI_SMTP_PORT e.g. 587
VENKAI_SMTP_USER / VENKAI_SMTP_PASSWORD Credentials
RESEND_API_KEY re_…, used when no SMTP host is set
VENKAI_EMAIL_FROM Venkai <noreply@app.venkai.fr>

Email is used for password reset and invitations. With neither backend configured, those flows cannot deliver — the API still accepts the request.

OAuth Beta

Variable Notes
GOOGLE_CONSOLE_CLIENT_ID / GOOGLE_CONSOLE_CLIENT_SECRET Google OAuth credentials
VENKAI_OAUTH_REDIRECT_BASE https://app.venkai.fr

GET /api/auth/oauth/providers reports what this deployment has configured. Query it rather than assuming.

Client-side

Not read by the server:

Variable Read by Default
VENKAI_API_KEY MCP server, your code
VENKAI_BASE_URL MCP server https://api.venkai.fr

The Python SDK reads neither — pass api_key and base_url to the constructor.

Effects worth knowing

VENKAI_ENV=production

flowchart LR
  E["VENKAI_ENV=production"] --> R["rate limiting ON<br/>300/min auth · 60/min anon"]
  E --> C["session cookie Secure=true"]

In development there is no rate limiting at all — so a 429 handler cannot be exercised against a dev instance. Test it against a production-mode one.

VENKAI_EMBEDDING_PROVIDER=semantic

Startup raises if sentence-transformers is absent rather than falling back to hashing. A silent downgrade would leave every answer looking plausible while the semantic matching the operator asked for was simply not happening.

Note the split default: the library defaults to hashing, the Docker image sets semantic. Reading the library docs and running the image gives you two different behaviours unless you set it explicitly.

VENKAI_DATABASE_URL

Switching engines does not migrate data. Run Alembic against the new target and migrate the rows yourself; GET /api/export is the supported way out.

Example .env

# ── Required ────────────────────────────────────────────────────────────
VENKAI_JWT_SECRET=<64 url-safe bytes  never commit this>
POSTGRES_PASSWORD=<32 url-safe bytes>

# ── Database ────────────────────────────────────────────────────────────
# Leave unset under docker-compose.prod.yml; it is composed for you.
# VENKAI_DATABASE_URL=postgresql://venkai:...@db:5432/venkai

# ── Core ────────────────────────────────────────────────────────────────
VENKAI_ENV=production
VENKAI_ALLOWED_ORIGINS=https://app.venkai.fr
VENKAI_EMBEDDING_PROVIDER=semantic

# ── Email (optional) ────────────────────────────────────────────────────
# RESEND_API_KEY=re_...
# VENKAI_EMAIL_FROM=Venkai <noreply@app.venkai.fr>

.env must never be committed

git check-ignore -v .env   # must print a .gitignore line
If that command prints nothing, stop and fix .gitignore before writing real values into the file.