Skip to main content

Documentation Index

Fetch the complete documentation index at: https://internal.september.wtf/llms.txt

Use this file to discover all available pages before exploring further.

This page brings the Engine up in production from a clean infrastructure slate. It assumes Docker (or any OCI-compatible runtime) and a place to mount a persistent volume for the brain database.

0. Prerequisites

  • Container runtime with persistent volume support.
  • HTTPS-terminating reverse proxy upstream (Caddy, nginx, ALB, Cloudflare).
  • Outbound HTTPS to your LLM provider(s).
  • Credentials set up:
    • LLM provider API key.
    • OpenAI API key for embeddings.
    • AD_ENCRYPTION_KEY (a Fernet key — see step 3).
    • ENGINE_KEY_HASH for the Engine’s own auth.

1. Build or pull the image

From the engine repo:
docker build -t engine:2.3.0 --target prod .
Or pull from your registry. Tag images with the engine version (the pyproject.toml version field).

2. Provision a volume for the brain

The brain SQLite file lives at /data/brain.sqlite inside the container. Mount a persistent volume there.
PlatformHow
Docker Composevolumes: ["engine_data:/data"]
KubernetesPVC mounted at /data
AWS FargateEFS access point at /data
Size the volume for at least 1 GB per active user. SQLite handles multi-GB brains comfortably; backup frequency matters more than ceiling.

3. Generate the Asset Directory encryption key

If you’ll use any MCP connectors:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Store the output as a secret (Vault, AWS Secrets Manager, k8s Secret). Inject it into the Engine as AD_ENCRYPTION_KEY. Do not commit it. Do not rotate it without a re-encrypt step — connections encrypted under the old key will not decrypt.

4. Generate the Engine API key hash

Generate a strong API key and its SHA-256 hash:
KEY=$(python -c "import secrets; print(secrets.token_urlsafe(48))")
echo "Plaintext: $KEY"
python -c "import hashlib; print(hashlib.sha256(open('/dev/stdin').read().strip().encode()).hexdigest())" <<< "$KEY"
Store the plaintext in your upstream router (BAP) and store the hash as ENGINE_KEY_HASH in the Engine. The plaintext never lives in the Engine’s environment.

5. Configure environment

Minimum for production:
# LLM
LLM_PROVIDER=anthropic
LLM_API_KEY=<from secret manager>
LLM_MODEL=claude-sonnet-4-5
OPENAI_API_KEY=<from secret manager>

# Auth
ENGINE_KEY_HASH=<from step 4>

# Asset Directory
AD_ENCRYPTION_KEY=<from step 3>
OAUTH_REDIRECT_BASE_URL=https://engine.your-domain.com

# Storage
SQLITE_DB_PATH=/data/brain.sqlite

# Hardening
ASSET_DIRECTORY_SANDBOXED=true
SUBPROCESS_SCRUB_MODE=strict
ENGINE_LOG_LEVEL=INFO
CORS_ORIGINS=https://app.your-domain.com
See Environment variables for the full catalog.

6. Run migrations

Migrations run automatically on Engine startup. Bring the Engine up once and watch the logs to confirm migrations applied cleanly.
docker run --rm -v engine_data:/data --env-file .env engine:2.3.0
If any migration fails, stop and investigate. Do not roll forward with a partial schema.

7. Bring up the service

Start the Engine in the foreground first, hit /health, then daemonize.
docker run -d \
  --name engine \
  -v engine_data:/data \
  -p 8000:8000 \
  --env-file .env \
  engine:2.3.0
Verify:
curl -fsS https://engine.your-domain.com/health
You should see {"status":"ok",...}.

8. Wire up your reverse proxy

Point your HTTPS-terminating proxy at localhost:8000 (or wherever the Engine listens). Required:
  • TLS termination.
  • Pass through X-Engine-Key header.
  • Long timeouts on /execute (SSE streams can run minutes).
  • No buffering of the response body (otherwise clients won’t see streaming).
Example nginx fragment:
location / {
    proxy_pass http://localhost:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_buffering off;
    proxy_read_timeout 1h;
    proxy_send_timeout 1h;
}

9. Schedule backups

The brain database holds the user’s entire memory. Back it up. Options:
  • Volume snapshots. Daily snapshot of the volume holding /data. Cheapest. Recovery is point-in-time.
  • /memory/export. Periodic API export to S3 (or equivalent). Restorable to a fresh Engine via /memory/import.
  • SQLite .backup. Run sqlite3 brain.sqlite .backup brain-$(date +%s).db inside the container on a schedule. Cheap and incremental-friendly.
Whichever you pick, test restores quarterly. A backup you’ve never restored is not a backup.

10. Configure observability

  • Health probe: GET /health every 10s.
  • Log shipping: configure the container to ship stdout/stderr to your logging stack.
  • Metrics: see Observability for what to scrape.
  • Alerts: see SLO definitions for the alerts that matter.

You’re live

A production Engine is running. Next steps: