This guide covers running the api.server:app FastAPI application in production.
# Build and run locally with docker-compose
docker-compose up --build
# API is then at http://localhost:8080
curl http://localhost:8080/healthThis sets up:
- SQLite-backed log store at
/data/cml.db(persisted in named volumecml-data) - Rate limiting enabled
- Healthcheck running every 10s
docker build -t causal-memory-layer:latest .The Dockerfile uses a two-stage build:
- build — installs
.[api]extras (fastapi, uvicorn, slowapi, python-multipart) into a virtualenv - runtime — minimal image with just the venv, a non-root user (
cml), and a healthcheck
Image size: ~250 MB.
All options are environment variables. Common ones:
| Variable | Default | Notes |
|---|---|---|
PORT |
8080 |
Listening port |
CML_STORE_PATH |
unset | SQLite DB path (unset = in-memory store). Example: /data/cml.db |
CML_STORE_TTL |
86400 |
Record TTL in seconds (1 day default). For persistent store only. |
CML_API_TOKEN |
unset | Bearer token for auth. If set, all endpoints except /health require Authorization: Bearer <token> |
CML_CORS_ORIGINS |
* (no auth) / `` (with auth) |
Comma-separated allowed origins. With auth enabled, defaults to deny all. |
CML_RATE_LIMIT_ENABLED |
true |
Enable slowapi rate limiting |
CML_RATE_LIMIT_DEFAULT |
60/minute |
Default per-key budget |
CML_DISABLE_DOCS |
unset | Set to 1 to hide /docs and /redoc |
# Generate an example local token and pass it via the environment.
export CML_API_TOKEN="$(openssl rand -hex 32)"
docker run \
-e PORT=8080 \
-e CML_STORE_PATH=/data/cml.db \
-e CML_API_TOKEN="${CML_API_TOKEN}" \
-e CML_CORS_ORIGINS="https://myapp.example.com" \
-v cml-data:/data \
-p 8080:8080 \
causal-memory-layer:latestThen use it:
curl -H "Authorization: Bearer ${CML_API_TOKEN}" \
http://localhost:8080/health-
Install flyctl:
curl -L https://fly.io/install.sh | sh -
Initialize your app:
flyctl auth login flyctl launch --image causal-memory-layer:latest --name cml-audit-api # (flyctl will guide you through region selection, secrets, volumes, etc.) -
Set secrets (for auth + CORS):
export CML_API_TOKEN="$(openssl rand -hex 32)" flyctl secrets set CML_API_TOKEN="${CML_API_TOKEN}" flyctl secrets set CML_CORS_ORIGINS="https://yourfrontend.com"
-
Enable persistent storage (optional):
flyctl volumes create cml_data --size 10 # Mount it in `fly.toml`: [mounts] source = "cml_data" destination = "/data" -
Deploy:
flyctl deploy
App will be live at https://cml-audit-api.fly.dev (or your chosen name).
-
Push this repo to GitHub (if not already there).
-
Create a new Web Service on Render:
- Connect your GitHub repo
- Set Build Command:
echo "Using Dockerfile" - Set Start Command: leave blank (Render will use EXPOSE + CMD from Dockerfile)
- Set Environment Variables:
PORT=10000(Render's default)CML_STORE_PATH=/data/cml.dbCML_API_TOKEN=...(if auth desired)
-
Add Disk (for persistent storage):
- In Service Settings → Disks, add a disk at
/data - Mount path:
/data
- In Service Settings → Disks, add a disk at
-
Deploy: Render will auto-deploy on push to main (or your chosen branch).
-
Connect your GitHub repo:
- New Project → GitHub repo
-
Select the repo and add a service:
- Railway auto-detects the Dockerfile
- Sets PORT to 8000 or 8080 automatically
-
Set environment variables:
CML_STORE_PATH=/data/cml.db CML_API_TOKEN=... -
Add a volume (for persistent store):
- New → Disk
- Mount path:
/data
-
Deploy: Auto-deployed on push.
The image can run on any container orchestrator. Example k8s Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cml-api
spec:
replicas: 2
selector:
matchLabels:
app: cml-api
template:
metadata:
labels:
app: cml-api
spec:
containers:
- name: cml-api
image: causal-memory-layer:latest
ports:
- containerPort: 8080
env:
- name: PORT
value: "8080"
- name: CML_STORE_PATH
value: /data/cml.db
- name: CML_API_TOKEN
valueFrom:
secretKeyRef:
name: cml-secrets
key: api-token
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: cml-data
mountPath: /data
volumes:
- name: cml-data
persistentVolumeClaim:
claimName: cml-data-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cml-data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10GiOnce deployed, the Audit API serves:
GET /health— healthcheck (no auth required)POST /audit— audit a JSONL logPOST /audit/file— audit an uploaded JSONL filePOST /ingest— append records to a named log storeGET /records/{log_name}— list records in a logGET /records/{log_name}/audit— run audit on a stored logGET /chain/{log_name}/{record_id}— reconstruct causal chainPOST /ctag/decode— decode a 16-bit CTAG valueGET /docs— Swagger UI (unlessCML_DISABLE_DOCS=1)GET /redoc— ReDoc (unlessCML_DISABLE_DOCS=1)
See api/server.py docstring for detailed endpoint descriptions and examples.
- Set
CML_API_TOKENto a strong random string (or use your platform's secret manager) - Set
CML_CORS_ORIGINSto your frontend domain (not*) - Enable
CML_STORE_PATHwith a persistent volume mount - Review
CML_RATE_LIMIT_*settings for your expected load - Set
CML_DISABLE_DOCS=1in production (hide swagger UI from public) - Monitor
/healthendpoint for liveness probes - Ensure logs are collected (via your platform's logging backend)
- Test the API with a real audit payload before going live
- Back up the SQLite DB regularly if using persistent store
Container exits immediately:
- Check logs:
docker logs <container_id> - Ensure
PORTenv var is set and accessible - Verify that
CML_STORE_PATHdirectory exists and is writable
POST /audit returns 422:
- Check that your JSONL is valid (one record per line)
- Ensure each record has required fields:
id,timestamp,actor,action,object,permitted_by - Use
GET /docsto test the endpoint with example payloads
Rate limiting too strict:
- Increase
CML_RATE_LIMIT_DEFAULT(e.g.,"120/minute") - Or disable:
CML_RATE_LIMIT_ENABLED=false(not recommended for public APIs)
SQLite DB locked:
- SQLite doesn't handle concurrent writes well
- For high concurrency, consider migrating to PostgreSQL (out of scope for this guide)
- Or increase
CML_STORE_TTLto reduce churn and run vacuums off-peak
- Integrate the API with your audit pipeline
- Use the SDK (
pip install causal-memory-layer) for local audit workflows - Check out
docs/for conceptual guides on CML rules and safe usage patterns