Configuration

All environment variables · ~12 min read v1.0 Phase 11 Phase 12

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 display name. Used for fallback display only post-Phase 12 — membership lookup is per-org via installations.
GITHUB_API_PAT Removed in Phase 12 Legacy long-lived Personal Access Token. Removed in Phase 12 — replaced by GitHub App JWT + installation tokens. verify-phase12.sh fails the deploy if any import remains in the codebase.

GitHub App Phase 12

The xbrain GitHub App handles all sign-in flows (web + Chrome extension) and all server-to-server calls to api.github.com on behalf of installed orgs. These six vars replace the legacy GITHUB_API_PAT + OAuth App env.

Variable Required Example value Description
GITHUB_APP_ID Yes 3743573 Numeric App registration ID — top of the App settings page.
GITHUB_APP_SLUG No xbrain URL slug used to build the install URL github.com/apps/<slug>/installations/new.
GITHUB_APP_CLIENT_ID Yes Iv23liVnZvIN0Lo6isof OAuth client ID — also used as the App JWT iss claim. Public value; the matching frontend constant is shared with app-site/account/teams/teams.js and the Chrome extension background.js.
GITHUB_APP_CLIENT_SECRET Yes OAuth client secret. Server-only — never exposed to browsers or the Chrome extension.
GITHUB_APP_PRIVATE_KEY_B64 Yes Base64-encoded private key PEM (single line). Generate: base64 -w 0 < xbrain.<date>.private-key.pem. Used by PyJWT[crypto] to RS256-sign App JWTs.
GITHUB_APP_WEBHOOK_SECRET Yes HMAC-SHA256 secret matching the value configured in the App settings. Used to verify X-Hub-Signature-256 on every /v1/webhooks/github/installation call. Generate: openssl rand -hex 32.

Token encryption uses FERNET_KEY

The ghu_ / ghr_ tokens persisted on the users table are Fernet-encrypted with FERNET_KEY (originally introduced in Phase 4 for drive-sync). Rotating FERNET_KEY invalidates every stored GitHub user token — every user must re-authorize. Do not rotate without comms.

Brain Monitor & Superadmin Phase 11

Phase 11 adds the universal Brain Monitor (per-team feed) and the /account/admin/ superadmin dashboard. Storage metrics rely on MinIO env vars already set in earlier phases; only ADMIN_USER_SUBS is new.

Variable Required Default Description
ADMIN_USER_SUBS No (empty) Comma-separated list of principal subs (e.g. github:mrboups,google:1087...) that may access /v1/admin/brain/*. Empty value disables every superadmin endpoint (lockdown kill-switch). Read at memory-api startup.
MINIO_URL No http://xbrain-minio:9000 S3 endpoint used by GET /v1/admin/brain/storage to report bytes per team. Unset → N/A in dashboard (no error).
MINIO_ACCESS_KEY No S3 access key for the same storage endpoint.
MINIO_SECRET_KEY No S3 secret key.
MINIO_BUCKET No Single bucket name — per-team prefix is computed inside.

brain-janitor needs no extra env

The brain-janitor daily cron container shares the same Postgres / Qdrant / Neo4j connection vars as memory-api. It reads them on startup. Heartbeat sentinel at /tmp/brain-janitor-alive must be refreshed within the last 25 hours.

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 Org display (legacy, optional post-Phase 12) ===
GITHUB_ORG=your-github-org
# GITHUB_API_PAT=     # REMOVED in Phase 12 — DO NOT set

# === Graphiti (Phase 5) ===
GRAPHITI_SERVICE_URL=http://graphiti-service:8300
SEMAPHORE_LIMIT=3

# === Brain Monitor + Superadmin (Phase 11) ===
# Comma-separated subs; empty value disables /v1/admin/brain/* entirely.
ADMIN_USER_SUBS=github:mrboups
# MINIO_URL etc. — already required from Phase 2 onwards.

# === GitHub App (Phase 12) ===
GITHUB_APP_ID=3743573
GITHUB_APP_SLUG=xbrain
GITHUB_APP_CLIENT_ID=Iv23liVnZvIN0Lo6isof
GITHUB_APP_CLIENT_SECRET=CHANGE_ME
GITHUB_APP_PRIVATE_KEY_B64=CHANGE_ME_SINGLE_LINE_BASE64
GITHUB_APP_WEBHOOK_SECRET=CHANGE_ME_32HEX

Next Steps