Configuration
Overview
All xbrain services are configured via environment variables defined in
infrastructure/.env. This file is gitignored — it is never committed to the
repository. An .env.example is provided with safe defaults and
CHANGE_ME placeholders for all secrets.
Never Commit .env
The infrastructure/.env file contains real secrets. It is gitignored by
design. Only infrastructure/.env.example (with placeholder values) is tracked
in git. Never commit your actual .env to the repository.
bashcp infrastructure/.env.example infrastructure/.env
# Edit .env and replace all CHANGE_ME values with your actual credentials
Core Database
PostgreSQL is the primary relational store for memory-api, events, audit logs, and team/user data. These variables are required on all phases.
| Variable | Required | Default | Description |
|---|---|---|---|
POSTGRES_USER |
Yes | xbrain | PostgreSQL username |
POSTGRES_PASSWORD |
Yes | — | PostgreSQL password. Use a strong random value. |
POSTGRES_DB |
Yes | xbrain | PostgreSQL database name |
DATABASE_URL |
Yes | — | Full async DSN: postgres+asyncpg://user:pass@postgres:5432/db |
memory-api
memory-api is the central memory service — all read/write operations from LibreChat, Open WebUI, and agents go through it. Several variables are phase-gated (Phase 3+, Phase 5).
| Variable | Required | Default | Description |
|---|---|---|---|
QDRANT_URL |
Yes | http://qdrant:6333 | Qdrant vector store URL (internal Docker DNS) |
QDRANT_API_KEY |
No | (empty) | Qdrant API key — leave empty unless Qdrant auth is enabled |
BRIDGE_SHARED_SECRET |
Yes | — | 32-char hex secret shared between memory-api and all bridges. Must match across services. Generate: openssl rand -hex 32 |
JWT_ALGORITHM |
No | HS256 | JWT signing algorithm used for bridge token verification |
LOG_LEVEL |
No | INFO | Logging verbosity: DEBUG, INFO, WARNING, ERROR |
ADMIN_USER_SUBS |
No | (empty) | Comma-separated Google sub IDs of admin users (grants admin endpoints) |
MEMORY_API_EXTERNAL_URL |
No | https://api.yourdomain.com | Public URL of memory-api — used to construct Drive webhook callback URLs |
GOOGLE_CLIENT_ID |
Yes | — | Google OAuth 2.0 client ID (from Google Cloud Console) |
GOOGLE_CLIENT_SECRET |
No | — | Google OAuth 2.0 client secret. Required if Drive sync is enabled (Phase 3+). |
OAUTH_CREDENTIALS_ENCRYPTION_KEY |
No | — | Fernet key for encrypting stored Drive OAuth tokens at rest. Required for Phase 3+. Generate: python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" |
NEO4J_URI |
No | bolt://neo4j:7687 | Neo4j Bolt connection URI (Phase 3+) |
NEO4J_USER |
No | neo4j | Neo4j username (Phase 3+) |
NEO4J_PASSWORD |
No | — | Neo4j password. Required when Neo4j is enabled (Phase 3+). |
GRAPHITI_SERVICE_URL |
No | http://graphiti-service:8300 | Graphiti temporal extraction service URL. memory-api fails soft if unreachable (Phase 5). |
GITHUB_ORG |
No | excalibur-game | GitHub Organization name — used to verify membership during GitHub Auth login (Phase 5) |
GITHUB_API_PAT |
No | — | GitHub Personal Access Token with read:org scope. Required for GitHub Auth (Phase 5). |
LibreChat
LibreChat connects to AI model providers via API keys. At least one model key is required for the chat interface to function.
| Variable | Required | Default | Description |
|---|---|---|---|
ANTHROPIC_API_KEY |
No | — | Anthropic API key for Claude models (sk-ant-...) |
OPENAI_API_KEY |
No | — | OpenAI API key for GPT models (sk-...) |
XAI_API_KEY |
No | — | xAI API key for Grok models |
MEILI_MASTER_KEY |
Yes | — | MeiliSearch master key (32+ chars). Generate: openssl rand -hex 32 |
LIBRECHAT_MONGO_URI |
Yes | — | MongoDB URI for LibreChat's conversation and user storage |
Bridge & Pipeline
The LibreChat bridge and Open WebUI pipeline forward conversations to memory-api.
BRIDGE_SHARED_SECRET must match the value set in memory-api.
| Variable | Required | Default | Description |
|---|---|---|---|
MEMORY_API_URL |
No | http://memory-api:8000 | Internal URL of memory-api (Docker service name) |
BRIDGE_DEFAULT_TEAM_SCOPE |
No | default | Default team_scope tag applied when user has no team assignment |
BRIDGE_BACKFILL_FROM |
No | startup | Backfill strategy for existing conversations: startup (on restart) or never |
PIPELINE_API_KEY |
Yes | — | Open WebUI pipeline API key. Must match Open WebUI's pipeline settings. Generate: openssl rand -hex 32 |
PIPELINE_DEFAULT_TEAM_SCOPE |
No | default | Default team_scope for Open WebUI pipeline writes |
AGENT_RUNTIME_URL |
No | http://agent-runtime:9100 | agent-runtime URL used by the Open WebUI pipeline for LangGraph tool calls |
agent-runtime
agent-runtime hosts LangGraph agents and connects to the MCP gateway for tool access.
| Variable | Required | Default | Description |
|---|---|---|---|
MCP_GATEWAY_URL |
No | http://mcp-gateway:8080 | MCP gateway URL for tool discovery and invocation |
MCP_TOOL_CACHE_TTL_SECS |
No | 300 | Seconds to cache the MCP tool list before re-fetching from gateway |
Langfuse (Phase 2)
Langfuse provides LLM observability — tracing, cost tracking, and prompt management. Keys are generated from the Langfuse UI after first boot.
| Variable | Required | Default | Description |
|---|---|---|---|
LANGFUSE_PUBLIC_KEY |
No | — | Langfuse project public key (from Langfuse UI → Settings → API Keys) |
LANGFUSE_SECRET_KEY |
No | — | Langfuse project secret key |
LANGFUSE_HOST in docker-compose.yml
LANGFUSE_HOST is hardcoded to http://langfuse:3000
(internal Docker DNS) in docker-compose.yml. This bypasses Cloudflare for SDK
calls — the SDK communicates directly with the Langfuse container. Do not change
this to the public URL.
Graphiti (Phase 5)
Graphiti performs temporal fact extraction from conversations and builds an episodic knowledge graph. memory-api calls it asynchronously and fails soft if the service is unavailable.
| Variable | Required | Default | Description |
|---|---|---|---|
GRAPHITI_SERVICE_URL |
No | http://graphiti-service:8300 | URL called by memory-api to submit conversation episodes. Fails soft if unreachable. |
SEMAPHORE_LIMIT |
No | 3 | Max concurrent Graphiti requests. Keep at 3 for Tier 1 API rate limits (OpenAI/Anthropic). |
Generating Secrets
Several variables require cryptographically random values. Use these commands to generate appropriate secrets for each:
bash — secret generation# Generate BRIDGE_SHARED_SECRET (32-byte hex)
openssl rand -hex 32
# Generate MEILI_MASTER_KEY (32-byte hex)
openssl rand -hex 32
# Generate PIPELINE_API_KEY (32-byte hex)
openssl rand -hex 32
# Generate NEO4J_PASSWORD (base64, 16-byte)
openssl rand -base64 16
# Generate OAUTH_CREDENTIALS_ENCRYPTION_KEY (Fernet key, required for Drive sync)
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Rotating BRIDGE_SHARED_SECRET
If you rotate BRIDGE_SHARED_SECRET, you must update the value in every
bridge and restart all services simultaneously. In-flight bridge JWTs signed with the
old secret will be rejected. Coordinate rotation during a maintenance window.
Secrets stored in .env must never be committed to git — add
infrastructure/.env to .gitignore.
Complete .env.example
This is the full infrastructure/.env.example as shipped in the repository.
Copy it to infrastructure/.env and replace every CHANGE_ME
value before starting any services.
infrastructure/.env.example# ================================================================
# xbrain — Environment Variable Reference
# Copy to infrastructure/.env and fill in your values.
# NEVER commit infrastructure/.env to git.
# ================================================================
# === Core Database ===
POSTGRES_USER=xbrain
POSTGRES_PASSWORD=CHANGE_ME
POSTGRES_DB=xbrain
DATABASE_URL=postgres+asyncpg://xbrain:CHANGE_ME@postgres:5432/xbrain
# === Qdrant (Vector Store) ===
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY=
# === Google OAuth (Required for SSO) ===
GOOGLE_CLIENT_ID=CHANGE_ME.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=CHANGE_ME
# === Security ===
BRIDGE_SHARED_SECRET=CHANGE_ME_32HEX
JWT_ALGORITHM=HS256
ADMIN_USER_SUBS=
# === AI Models ===
ANTHROPIC_API_KEY=sk-ant-CHANGE_ME
OPENAI_API_KEY=sk-CHANGE_ME
XAI_API_KEY=
# === LibreChat ===
MEILI_MASTER_KEY=CHANGE_ME_32HEX
LIBRECHAT_MONGO_URI=mongodb://mongo:27017/librechat
# === Bridge & Pipeline ===
MEMORY_API_URL=http://memory-api:8000
BRIDGE_DEFAULT_TEAM_SCOPE=default
BRIDGE_BACKFILL_FROM=startup
PIPELINE_API_KEY=CHANGE_ME_32HEX
PIPELINE_DEFAULT_TEAM_SCOPE=default
AGENT_RUNTIME_URL=http://agent-runtime:9100
# === agent-runtime ===
MCP_GATEWAY_URL=http://mcp-gateway:8080
MCP_TOOL_CACHE_TTL_SECS=300
# === Langfuse (Phase 2 — generate from Langfuse UI after first boot) ===
LANGFUSE_PUBLIC_KEY=
LANGFUSE_SECRET_KEY=
# === Neo4j (Phase 3) ===
NEO4J_URI=bolt://neo4j:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=CHANGE_ME
# === Drive Sync Encryption (Phase 3) ===
OAUTH_CREDENTIALS_ENCRYPTION_KEY=
# === External URLs ===
MEMORY_API_EXTERNAL_URL=https://api.yourdomain.com
# === Logging ===
LOG_LEVEL=INFO
# === GitHub Auth (Phase 5) ===
GITHUB_ORG=your-github-org
GITHUB_API_PAT=
# === Graphiti (Phase 5) ===
GRAPHITI_SERVICE_URL=http://graphiti-service:8300
SEMAPHORE_LIMIT=3
Next Steps
- Deployment guide — Step-by-step instructions for Phase 1 through Phase 5, including VM sizing and Docker setup.
- Memory System — How the 7-field tagging contract is applied to every data point stored by memory-api.
- Drive Sync — Configuring OAuth credentials for Google Drive folder synchronisation.
- GitHub Auth — Setting up GitHub Org membership verification for team access control.