Skip to content

Latest commit

 

History

History
329 lines (232 loc) · 12.7 KB

File metadata and controls

329 lines (232 loc) · 12.7 KB

Deployment Guide

This guide covers running Statewave in different environments.

Statewave is vendor-neutral: the API is a plain Python / ASGI process that reads its config from STATEWAVE_* environment variables and talks to a Postgres database. Anywhere you can run a Python or Docker process — your laptop, a VPS, a container platform, Kubernetes, a hyperscaler — Statewave runs the same way. The Docker single-container recipe (Section 3) is the canonical production deploy; the Fly.io and Railway sections that follow are concrete examples of that recipe on two specific platforms, not a preferred path.

Hardware: Statewave's API process is CPU-only — no GPU is required, and none of the deployment recipes below assume one. GPUs only enter the picture if you choose to self-host an LLM compiler or embedding model. See Hardware & Scaling.

Sizing: for tier-by-tier hardware profiles (local / small production / growing / enterprise) and topology patterns, see the Deployment Sizing Guide.

Already running, things are slow? See the Capacity Planning & Tuning Checklist — diagnostic flow, tuning order, and when to move up a tier.


1. Local Development (Docker Compose)

The fastest way to run Statewave locally.

Prerequisites

  • Docker & Docker Compose
  • Git

Steps

git clone https://github.com/smaramwbc/statewave.git
cd statewave

# Copy and optionally edit environment config
cp .env.example .env

# Start Postgres + API
docker compose up -d

# Run migrations
docker compose exec api alembic upgrade head

# Verify
curl http://localhost:8100/healthz```

The API is available at `http://localhost:8100`. Postgres data persists in the `pgdata` volume.

### Useful commands

```bash
docker compose logs -f api     # tail API logs
docker compose down            # stop services
docker compose down -v         # stop + delete data

2. Local Development (bare metal)

Run without Docker — useful for debugging or IDE integration.

Prerequisites

  • Python 3.11+
  • PostgreSQL 16 with pgvector extension
  • Git

Steps

git clone https://github.com/smaramwbc/statewave.git
cd statewave

# Create and activate venv
# Windows users (powershell): `python -m venv .venv ; .\.venv\Scripts\Activate.ps1` 
# Windows users (cmd): `python -m venv .venv && .venv\Scripts\activate.bat`
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,llm]"

# Start Postgres (or use docker compose up db -d for just the database).
# As a Postgres superuser, create the role and database, then install the
# pgvector extension into that database. CREATE EXTENSION requires a
# superuser; the statewave app role itself does not need elevated rights:
#   CREATE ROLE statewave WITH LOGIN PASSWORD 'statewave';
#   CREATE DATABASE statewave OWNER statewave;
#   \c statewave
#   CREATE EXTENSION IF NOT EXISTS vector;

# Set connection string
# Windows (powershell): `$env:STATEWAVE_DATABASE_URL = "postgresql+asyncpg://statewave:statewave@localhost:5432/statewave"`
# Windows (cmd):        `set STATEWAVE_DATABASE_URL=postgresql+asyncpg://statewave:statewave@localhost:5432/statewave`
export STATEWAVE_DATABASE_URL=postgresql+asyncpg://statewave:statewave@localhost:5432/statewave

# Run migrations
alembic upgrade head

# Start server
uvicorn server.app:app --reload --port 8100

3. Single-Container Deployment (canonical production recipe)

Deploy Statewave as a single container pointing to an external Postgres with the pgvector extension. This is the recipe every platform-specific section below builds on — the only thing that changes from one platform to the next is how you set the environment variables and who runs the container.

docker build -t statewave .

docker run -d \
  --name statewave \
  -p 8100:8100 \
  -e STATEWAVE_DATABASE_URL=postgresql+asyncpg://user:pass@your-db-host:5432/statewave \
  -e STATEWAVE_DEBUG=false \
  -e STATEWAVE_API_KEY=your-secret-key \
  statewave

Run migrations against the remote database before first start:

docker run --rm \
  -e STATEWAVE_DATABASE_URL=postgresql+asyncpg://user:pass@your-db-host:5432/statewave \
  statewave \
  alembic upgrade head

4. Fly.io (platform example)

One concrete platform recipe for the single-container deploy in Section 3. Choosing Fly is a vendor decision — Statewave itself has no Fly-specific code path. Skip this section if you're deploying elsewhere; nothing in the rest of the docs depends on Fly.

Prerequisites

  • flyctl CLI installed and authenticated
  • A Fly Postgres cluster (or external Postgres)

Steps

cd statewave

# Create app
fly launch --no-deploy

# Set secrets
fly secrets set \
  STATEWAVE_DATABASE_URL=postgresql+asyncpg://user:pass@your-fly-db.internal:5432/statewave \
  STATEWAVE_API_KEY=your-secret-key

# Deploy
fly deploy

# Run migrations (one-off machine)
fly machine run . --command "alembic upgrade head" --rm

A minimal fly.toml:

[http_service]
  internal_port = 8100
  force_https = true

[env]
  STATEWAVE_HOST = "0.0.0.0"
  STATEWAVE_PORT = "8100"
  STATEWAVE_DEBUG = "false"

5. Railway (platform example)

Another concrete recipe for the single-container deploy in Section 3. Railway provisions Postgres for you and wires it through. As with Fly, this section is a worked example, not a recommendation.

Steps

  1. Connect your statewave repo to Railway
  2. Add a Postgres service (Railway provisions pgvector-capable Postgres)
  3. Set environment variables:
    • STATEWAVE_DATABASE_URL → Railway provides DATABASE_URL; convert to asyncpg format: replace postgresql:// with postgresql+asyncpg://
    • STATEWAVE_API_KEY → your secret
    • STATEWAVE_DEBUGfalse
  4. Set start command: alembic upgrade head && uvicorn server.app:app --host 0.0.0.0 --port $PORT
  5. Deploy

6. Kubernetes (in-tree Helm chart)

Another concrete recipe for the single-container deploy in Section 3 — this one runs the API as a Deployment on Kubernetes. The chart is API-only; you bring a pgvector-capable Postgres reachable from the cluster.

The full walkthrough — secret-management patterns, Postgres options, Ingress timeout cheatsheet, HPA and connection-budget guidance, k8s-specific troubleshooting — lives in the dedicated Kubernetes Deployment page. The five-second sketch:

helm install statewave \
  https://github.com/smaramwbc/statewave/releases/download/helm-0.1.0/statewave-0.1.0.tgz \
  --namespace statewave --create-namespace \
  --set database.url='postgresql+asyncpg://USER:PASS@db.example.com:5432/statewave' \
  --set llm.apiKey='sk-…' \
  --set auth.apiKey='replace-me'

Schema migrations run as a Helm pre-install + pre-upgrade Job (alembic upgrade head); the Deployment never serves traffic against an out-of-date schema. For multi-replica deploys, walk the connection-budget runbook in horizontal-scaling.md before raising replicaCount.


Environment Variables Reference

See .env.example for all available STATEWAVE_* configuration options.

LLM and embedding provider configuration

Two settings decide whether you get real semantic memory: the compiler and the embedding provider. Both route through LiteLLM, so any LiteLLM-supported provider works by changing the model identifier. Without either, Statewave runs in demo mode (regex extraction + hash-based embeddings, no semantic search).

Hosted provider (OpenAI, Anthropic, Azure, Bedrock, …)

STATEWAVE_COMPILER_TYPE=llm
STATEWAVE_EMBEDDING_PROVIDER=litellm
STATEWAVE_LITELLM_API_KEY=sk-...
STATEWAVE_LITELLM_MODEL=gpt-4o-mini
STATEWAVE_LITELLM_EMBEDDING_MODEL=text-embedding-3-small

Local, no API key (Ollama / self-hosted)

A model identifier starting with ollama/ tells Statewave the provider is local: it leaves STATEWAVE_LITELLM_API_KEY unset by design, suppresses the missing-key startup warning, and makes /readyz probe the local server instead of reporting a missing key (issue #122).

STATEWAVE_COMPILER_TYPE=llm
STATEWAVE_LITELLM_MODEL=ollama/llama3
STATEWAVE_LITELLM_API_BASE=http://host.docker.internal:11434   # API container → host Ollama

host.docker.internal resolves on Docker Desktop; on Linux add extra_hosts: ["host.docker.internal:host-gateway"] to the api service or use the host's LAN IP.

Embedding dimensions — the local-embeddings constraint

The pgvector column is fixed at 1536 dimensions (migration 0013; changing it means ALTERing the column type and rebuilding the HNSW index). STATEWAVE_LITELLM_EMBEDDING_MODEL must produce vectors of that size — Statewave does not resize the provider's output.

Embedding source Native dims Works against the default schema?
OpenAI text-embedding-3-small / -large 1536 (configurable) Yes
Ollama nomic-embed-text 768 No — insert fails without a migration
Ollama mxbai-embed-large 1024 No — insert fails without a migration

A fully key-free deployment therefore has three honest options:

  1. Ollama compiler + STATEWAVE_EMBEDDING_PROVIDER=stub — LLM extraction is local; retrieval is keyword/text only (no semantic vectors). Simplest, zero egress. Good for first-touch local exploration.
  2. Ollama compiler + hosted embedding provider — compilation stays local; only short embedding inputs leave the network. Keeps semantic search. Best when semantic recall matters but egress should be minimal.
  3. Ollama compiler + local embedding model + a schema migration to the model's native dimension. Fully local semantic search, at the cost of maintaining a custom migration. Only when zero egress is a hard requirement.

See Compiler Modes for the compiler trade-offs and Privacy & Data Flow for what each configuration sends where.

Production Checklist

  • STATEWAVE_DEBUG=false
  • STATEWAVE_API_KEY set to a strong secret
  • STATEWAVE_CORS_ORIGINS restricted to your domain(s)
  • STATEWAVE_RATE_LIMIT_RPM set (e.g., 120)
  • STATEWAVE_KIND_TTL_DAYS configured if you want stale memories to stop influencing /v1/context automatically — see Memory TTL
  • Database backups configured
  • Migrations run before first request
  • Health endpoint monitored (GET /healthz)

Admin Console (optional)

For operator visibility into your running Statewave instance, you can deploy the admin console.

The admin console provides:

  • System readiness and database health status
  • Compile job monitoring
  • Webhook delivery status
  • Usage metering (episodes, memories, compiles)
  • Subject health distribution

How to run it

Three paths, all equivalent — pick whichever fits your deployment:

1. Bundled with the server via the default compose stack (recommended for self-hosters):

The statewave/docker-compose.yml stack includes an admin service that pulls statewavedev/statewave-admin from Docker Hub. docker compose up -d brings it up alongside the API.

2. Standalone Docker container (any orchestrator — Kubernetes, Nomad, ECS, App Runner, Cloud Run, Render, …):

docker run -d --name statewave-admin -p 8080:8080 \
  -e STATEWAVE_API_URL=https://your-statewave-instance \
  -e STATEWAVE_API_KEY=$STATEWAVE_API_KEY \
  -e ADMIN_PASSWORD=$ADMIN_PASSWORD \
  -e ADMIN_SESSION_SECRET=$ADMIN_SESSION_SECRET \
  statewavedev/statewave-admin:latest

3. From source (Node ≥18, useful when contributing to the admin codebase):

See statewave-admin/README.md.

Production-mode auth

In production you must set both ADMIN_PASSWORD and ADMIN_SESSION_SECRET, and must not set ADMIN_AUTH_DISABLED=true. The compose default ships ADMIN_AUTH_DISABLED=true for local dev — production deployments override this by leaving it empty in your .env and supplying real values for the other two:

ADMIN_AUTH_DISABLED=          # required: leave empty
ADMIN_PASSWORD=$(openssl rand -base64 32)
ADMIN_SESSION_SECRET=$(openssl rand -hex 32)

Important: even with the password gate enabled, the admin console is an internal tool. Deploy it behind an access gateway (Cloudflare Access, OAuth2 Proxy, identity-aware proxy, …) — do not expose publicly. See statewave-admin/SECURITY.md for the full security posture.