Statewave is an open-source memory runtime for AI agents. It records raw events, compiles them into structured memories, and assembles ranked, token-bounded context bundles you can drop straight into a prompt — so your agent stops forgetting across sessions.
This guide gets a local server running with Docker Compose, then stores and retrieves your first memory. It takes about 5 minutes and needs no API key — just Docker.
The fastest path to a running Statewave is Docker Compose. It starts the API and a Postgres database, and runs the database migrations for you.
Prerequisites — one thing:
- Docker Desktop (macOS / Windows), or Docker Engine with the Compose plugin (Linux).
You do not need Python, Node, an LLM API key, or any SDK to finish this guide. Those come later, and every one of them is optional.
git clone https://github.com/smaramwbc/statewave.git
cd statewave
cp .env.example .env
docker compose up -dThat is the whole setup. docker compose up -d pulls the images, starts
Postgres + the API, and runs migrations automatically on first boot. The very
first start can take a minute or two while the images download.
No API key is needed for this guide. The copied
.envleaves the LLM key blank, so Statewave starts in demo mode — a local regex compiler and hash-based embeddings, with no external calls. Every step below works in demo mode. You will see onellm_compiler_missing_api_keywarning line in the logs; that is expected, not an error. To switch on real LLM extraction and semantic search later, see Turn on a real LLM provider.
curl http://localhost:8100/healthz
# → {"status":"ok"}
curl http://localhost:8100/readyz
# → {"status":"ready","checks":[ ... ]}/healthz— the API process is alive./readyz— the API and its database are ready to serve requests.
If /readyz is not ready yet, wait about 10 seconds (Postgres is still
starting on the first run) and try again. Still stuck? Jump to
If something goes wrong.
Statewave's data model is three steps: ingest raw episodes → compile them into memories → retrieve ranked context. Steps 3 and 4 do all three.
First, ingest an episode — a raw interaction record Statewave will remember:
curl -X POST http://localhost:8100/v1/episodes \
-H "Content-Type: application/json" \
-d '{
"subject_id": "user-1",
"source": "chat",
"type": "conversation",
"payload": {"messages": [
{"role": "user", "content": "My name is Alice and I work at Globex Corp."}
]}
}'A subject is any entity you track — a user, account, agent, or repo. Here
it is user-1.
Now compile — turn raw episodes into structured memory:
curl -X POST http://localhost:8100/v1/memories/compile \
-H "Content-Type: application/json" \
-d '{"subject_id": "user-1"}'
# → {"subject_id":"user-1","memories_created":1,"memories":[ ... ]}memories_created should be 1 or more. Compilation is idempotent —
running it again only processes new episodes.
Ask Statewave to assemble context for a task:
curl -X POST http://localhost:8100/v1/context \
-H "Content-Type: application/json" \
-d '{"subject_id": "user-1", "task": "Who is this user?", "max_tokens": 500}'The response's assembled_context field is a ready-to-use string for an LLM
prompt — it contains what Statewave compiled about user-1, including the
facts that Alice works at Globex Corp.
That is the whole loop — ingest → compile → retrieve. You now have a working Statewave server. Everything below is optional.
| Symptom | Fix |
|---|---|
docker: command not found, or Cannot connect to the Docker daemon |
Docker is not installed or not running. Install / open Docker Desktop, wait until it reports "running", then retry. |
Error ... port is already allocated / bind: address already in use on 8100 |
Another process owns port 8100. Free it, or run on a different host port: add STATEWAVE_API_HOST_PORT=8101 to .env, run docker compose up -d again, and use localhost:8101 everywhere below. |
curl: (7) Connection refused on localhost:8100 |
The API container is not up yet. Check docker compose ps (the api service should be running) and docker compose logs api. On first boot, also give Postgres ~10 seconds to start. |
/readyz shows the database check failing, or returns 503 |
Postgres is not healthy yet. Run docker compose ps — the db service should be healthy — and docker compose logs db. The first start of the pgvector image can take 10–20 seconds. |
cp: .env.example: No such file or directory |
You are not in the statewave directory. Run cd statewave first (the folder git clone created). |
.env is missing |
The server still boots on built-in defaults, but cp .env.example .env is the documented path. Re-run it from inside the statewave directory. |
/readyz shows "llm" ... "STATEWAVE_LITELLM_API_KEY is not set" |
Expected in demo mode — there is no key, so there is nothing to check. Overall status is still ready. Set a key only when you want real LLM extraction (see the next section). |
You set an API key in .env but it is not picked up |
.env must sit next to docker-compose.yml, and containers read it only at start. After editing .env, run docker compose up -d again to recreate the containers. |
Ollama: host.docker.internal does not resolve (Linux) |
On Linux without Docker Desktop, add extra_hosts: ["host.docker.internal:host-gateway"] to the api service in docker-compose.yml, or point STATEWAVE_LITELLM_API_BASE at your host's LAN IP. |
422 validation_error |
The request body is missing a required field — subject_id, source, type, and payload are all required on /v1/episodes. |
401 missing_api_key |
The server has STATEWAVE_API_KEY set. Send it as an X-API-Key header, or unset it in .env for open local dev. |
Reset everything. To wipe all containers and data and start clean:
docker compose down -vThe -v flag also deletes the Postgres volume, so every episode and memory is
removed. Then run docker compose up -d again for a fresh server.
For production-side issues, see deployment troubleshooting.
The quickstart used curl so it works with zero install. For real
applications, use a typed SDK — both wrap the same HTTP API. This step is
optional; skip it if you are calling the API directly.
Python — pip install statewave:
from statewave import StatewaveClient
with StatewaveClient("http://localhost:8100") as sw:
sw.create_episode(
subject_id="user-1",
source="chat",
type="conversation",
payload={"messages": [
{"role": "user", "content": "My name is Alice and I work at Globex Corp."}
]},
)
sw.compile_memories("user-1")
ctx = sw.get_context("user-1", task="Who is this user?", max_tokens=500)
print(ctx.assembled_context)TypeScript — npm install @statewavedev/sdk:
import { StatewaveClient } from "@statewavedev/sdk";
const sw = new StatewaveClient("http://localhost:8100");
await sw.createEpisode({
subjectId: "user-1",
source: "chat",
type: "conversation",
payload: { messages: [
{ role: "user", content: "My name is Alice and I work at Globex Corp." },
]},
});
await sw.compileMemories("user-1");
const ctx = await sw.getContext({
subjectId: "user-1",
task: "Who is this user?",
maxTokens: 500,
});
console.log(ctx.assembledContext);The SDKs also cover batch ingestion, timelines, search, subject deletion, authentication, multi-tenancy, and audit receipts. Full reference:
- statewave-py — Python SDK (sync + async clients)
- statewave-ts — TypeScript SDK
- API v1 contract — every endpoint, for any language over plain HTTP
Demo mode is perfect for a first run, but its regex compiler and hash-based embeddings do not do real semantic search. For production-quality memory, give Statewave an LLM provider. There are three modes:
| Mode | What you get | Setup |
|---|---|---|
| Demo mode (default) | Local regex extraction, no semantic search, zero external calls, no key. | Nothing — this is .env.example as shipped. |
| Hosted provider | Real LLM extraction + semantic search via OpenAI, Anthropic, Azure, Bedrock, and 100+ others. | Set one API key in .env. |
| Ollama (local LLM) | Real LLM extraction on your own machine — no key, no external calls. | Point Statewave at a local Ollama server. |
To use a hosted provider, edit .env:
STATEWAVE_COMPILER_TYPE=llm
STATEWAVE_EMBEDDING_PROVIDER=litellm
STATEWAVE_LITELLM_API_KEY=sk-... # your provider key
STATEWAVE_LITELLM_MODEL=gpt-4o-mini # any LiteLLM model id
STATEWAVE_LITELLM_EMBEDDING_MODEL=text-embedding-3-smallThen run docker compose up -d again to pick up the change. Statewave routes
every call through LiteLLM, so switching
providers is just a different model id.
For Ollama, local embedding dimensions, and the full provider matrix, see LLM and embedding provider configuration.
Confirm a key was picked up: run
curl http://localhost:8100/readyz. When a key is set, thellmcheck makes a real provider call. If it still saysSTATEWAVE_LITELLM_API_KEY is not set, the key is not being read — check that.envsits next todocker-compose.ymland re-rundocker compose up -d.
For local development Statewave runs in open-access mode. To require
authentication, set a key in .env:
STATEWAVE_API_KEY=my-secret-key-123Run docker compose up -d again, then send the key on every request with the
X-API-Key header (curl -H "X-API-Key: my-secret-key-123" ...), or pass it
to the SDK client constructor.
The default docker compose up -d already starts an operator console
alongside the API:
- API —
http://localhost:8100 - Admin console —
http://localhost:8080— browse subjects, episodes, and memories
The compose file ships ADMIN_AUTH_DISABLED=true so a fresh start needs no
password. Do not expose this admin to the internet without setting one —
see statewave-admin for
production auth.
Want only the core API + database? Run docker compose up -d api db.
The marketing website and interactive demo is a separate app — see statewave-web to run it from source.
- Runnable examples — statewave-examples: a minimal quickstart, support agents, a coding agent, evals, and benchmarks.
- Interactive API docs —
http://localhost:8100/docs(Swagger) once the server is running. - Feed real data in — Connectors ingest GitHub, Markdown/docs, Slack, support tickets, email, and more as episodes — with no custom ingest code.
- Understand the design — Architecture overview · Compiler modes · Privacy & data flow.
- Go to production — Deployment guide · Sizing guide · Deployment troubleshooting.
- Every config option —
.env.examplelists allSTATEWAVE_*settings.