Everything on this page is copy-pasteable. It covers installing Areev on each registry, the three-command CLI quickstart, wiring the MCP server into Claude Code, embedding the store from Rust / Python / Node, the PostgreSQL backend, encryption at rest, and durability/fleet sync. Task-oriented recipes beyond these live in the cookbook.
Areev ships on all three registries — install the surface you need:
cargo install areev # the `areev` CLI
pip install areev # Python bindings
npm install @areev/areev # Node bindings (unscoped `areev` is pending an npm exception)No Rust toolchain? Every release also carries prebuilt areev binaries for
Linux (x86_64 / aarch64), macOS (Intel / Apple Silicon) and Windows x86_64:
curl -fsSL https://raw.githubusercontent.com/AreevAI/areev/main/scripts/install.sh | shIt installs to ~/.local/bin (/usr/local/bin as root; override with
AREEV_INSTALL), pins with AREEV_VERSION=v1.0.2, and verifies the download
against the release's SHA256SUMS.
Two things to know about the Linux assets. They need GLIBC 2.39 or newer
(Ubuntu 24.04+, Debian 13+, RHEL 10+) — on Ubuntu 22.04 the dynamic linker
fails with a bare GLIBC_2.39 not found; use the container image or
cargo install there. And the default build carries neither the Postgres
backend nor TLS, which is what keeps the edge binary dependency-light. For
the server tier there is a second Linux asset with both compiled in:
AREEV_FLAVOR=postgres curl -fsSL https://raw.githubusercontent.com/AreevAI/areev/main/scripts/install.sh | shOr grab an archive straight from the
Releases page — handy in a
notebook, where the wheel covers the memory and the loop but areev ui (the
web console, including the review queue) lives in the binary.
Or run it as a container — the repo's Dockerfile builds
the binary with the Postgres and TLS features already compiled in (a stock
install has neither):
git clone https://github.com/AreevAI/areev && cd areev
docker build -t areev .
docker run --rm -v areev-data:/data areev add john prefers "window seat"The image serves every role — console and a trigger heartbeat —
and maps onto AWS / GCP / Azure / Kubernetes: docker.md.
Embedding the store in a Rust project? Add the library crates instead of the CLI:
cargo add areev-store areev-coreOr build from source (Rust 1.90+):
git clone https://github.com/AreevAI/areev
cd areev
cargo build --release # builds the `areev` binary
./target/release/areev --help
# Python bindings (maturin): maturin develop -m crates/areev-py/Cargo.toml
# Node bindings (napi-rs): cd crates/areev-js && npm ci && npm run buildStore a fact, recall it, hand it to a model — no ceremony (--db is optional;
it falls back to $AREEV_DB, then ~/.areev/default.db):
areev add john prefers "window seat" # subject relation object
areev recall john # → the stored fact, one JSON grain per line
areev recall john --render sml # → "john prefers window seat" as a model-ready blockPoint it at a specific file with -d mem.db (or export AREEV_DB=mem.db).
Then explore: areev cal '<QUERY>' runs the query language
(reference), areev ui opens the web console
(http://127.0.0.1:7437), and areev repl is an interactive CAL shell. The
global --embed-cmd 'my-embedder' flag installs your embedder for vector
recall on any verb (text on stdin, JSON vector on stdout).
claude mcp add areev -- areev serve --mcp --db ~/.areev/code.db --ns claude-codeareev serve --mcp speaks newline-delimited JSON-RPC 2.0 on stdio and works
with any MCP client — 25 tools, with a 12-tool --profile memory subset for
hosts that only want chat memory. See mcp-reference.md.
Memory is half the story. The other half is executing agents so that what they did is provable afterwards — journaled, resumable, replayable, and gated by humans where it matters. The 10-minute proof needs no LLM key:
areev run demo --db runs.db # seeds a 2-node plan: host tool → human approval
# (prints the workflow hash — a content-addressed grain)
areev run start --db runs.db --workflow <WF_HASH> --run-id demo-1 \
--input '{"who":"world"}' --tool-cmd 'printf '\''{"greeting":"hello"}'\'''
# → the host tool runs, then the run PARKS on the approval gate:
# {"kind":"requires_action","asks":[{"node":"approve","tool_call_id":"<ASK>",…}],…}Approve it — as a different principal, because the principal who started the run structurally cannot approve their own ask:
areev run respond --db runs.db --run-id demo-1 --ask <ASK> \
--result '{"approved":true}' --as user:officer
areev run resume --db runs.db --run-id demo-1 # → {"finished":"Completed"}
areev run verify --db runs.db --run-id demo-1 # replays the journal, byte-compares every checkpointThat verify is the point: every step wrote an intent grain before
dispatch and a result grain that supersedes it, plus a checkpoint per
superstep — so the run can be re-derived from its own journal and compared
byte-for-byte against what was stored. If anyone edited history, verify names
the checkpoint and the differing fields. From there, everything is a query,
not a log grep:
areev run-trace --run-id demo-1 # the full journal, in order
areev runs-touching --hash <HASH> # which runs produced/refined this grain (the reverse join)
areev run oversight-report --run-id demo-1 # the EU AI Act Art. 14 answers: gates, budgets,
# responders, MEASURED kill-switch drain time
areev run cancel --run-id demo-1 # the kill switch (lowest-privilege verb)
areev run fork --run-id demo-1 --as-run demo-1b --at 1 # time-travel: branch from superstep 1
areev run shadow --runs demo-1 # re-execute from the journal with ZERO side effectsReal plans go further than the demo, with the same guarantees:
- LLM nodes: leave a node unbound and it becomes an abstract node — a
journaled tool-calling loop (
--model claude-sonnet,openai:gpt-5,ollama:llama3.1, or any OpenAI-compatible endpoint; keys from the environment). Every model turn and tool call lands in the journal, so verify never needs to call the model. - Budgets that actually stop the run:
--max-tokens / --max-usd / --max-wall-ms / --max-supersteps. A budget-exhausted run parks at a checkpoint;areev run forkre-opens it under raised budgets exactly where it stopped. - LangGraph-grade control flow: conditional edges, bounded cycles,
Sendfan-out, subgraphs, typed reducers (append/sum/max/…), streaming events — all validated at plan load, all replayable. - Every surface: the same six verbs ride the MCP
server (
areev_run_*— host tools execute only via$AREEV_RUN_TOOL_CMD), the Python/Node bindings (db.run_start(…)/await m.runStart(…)), and the web console's Runs tab, which is the human approval queue (shared-token and anonymous callers are refused for approvals — the approver's identity is the audit record).
Because the plan, the journal, and the memory share one file, the run/memory
join comes free: an agent's tool call cites the run that made it, and a
fact's provenance names the runs that touched it. Full guide:
run.md · standing rules that start runs on a schedule or an
event: triggers.md · compliance maps:
eu-ai-act.md, procurement.md.
You don't have to adopt the runtime to get the governance. Two pip adapters —
areev-langgraph and
areev-crewai — put Areev
underneath the framework you already run:
# LangGraph: a checkpointer where one thread = one memory file you can
# diff, sync, and erase; plus a BaseStore and a trace mirror.
from areev_langgraph import AreevCheckpointSaver
graph = builder.compile(checkpointer=AreevCheckpointSaver("./threads"))
# CrewAI: memory storage where every consolidation rewrite is a supersession
# — "what did the agent believe before the LLM rewrote it" stays a query.
from crewai.memory import Memory
from areev_crewai import AreevStorageBackend
memory = Memory(storage=AreevStorageBackend("crew.db"))What that buys you over the in-memory/SQLite defaults: checkpoints form
supersession trees (time-travel and re-put both work, history kept); a
CrewAI record's source becomes a partition-keyed subject, so one
areev forget-subject "<source>" erases that user's records, history, and
index rows with a receipt — the right-to-erasure demo; and the trace/audit
mirrors are honest about loss: best-effort mode counts every dropped event,
guaranteed mode backpressures and never drops (the only mode a compliance
story may cite).
Both adapters live outside this repo and are not under active development — 1.0.0 is on PyPI and works against current Areev, but new releases wait on demand. If you want either moved forward, say so in an issue and we will un-park them.
Most agent hosts are async (Tokio, axum). Use AsyncAreev there — it runs each
operation on the blocking pool and tears the store down off the async worker, so
neither a call nor a drop can panic inside a runtime:
use areev_store::AsyncAreev;
use areev_core::types::Fact;
let db = AsyncAreev::open("agent.db").await?;
db.add(Fact::new("john", "prefers", "dark mode")).await?;
let latest = db.latest("caller", "john", "prefers").await?;In synchronous code (a CLI, a script, a test) use Areev directly:
use areev_store::Areev;
use areev_core::types::Fact;
let mut db = Areev::open("agent.db")?;
db.add(&Fact::new("john", "prefers", "dark mode"))?;
Areevis blocking and drives its own runtime, so it must not be called — or dropped — from inside an async runtime. Reach forAsyncAreevin async code.
import areev, json
m = areev.Areev("john.db", ns="caller")
m.add_fact("john", "prefers", "tea", confidence=0.95)
m.recall("john") # JSON string, newest-first — needs a subject
m.search("tea", k=5) # free text, when you don't have a subject.
# BM25-only out of the box, so it matches
# words that are present; install an
# embedder for semantic hits like
# "hot drinks".
m.cal('RECALL facts WHERE subject = "john"')
m.memory_tool(json.dumps({"command": "view", "path": "/memories"})) # Anthropic memory-tool backendAreev(..., index_text=False) turns the BM25 index off for this file (a
deliberate re-stamp, reported by open_warnings()). That trades search()'s
text leg — keep it working by installing an embedder — for write latency that
stays flat as the file grows. add_batch(...) writes many grains in one
transaction; to load another system's export, prefer migrate()
(migration guide).
const { Areev } = require('@areev/areev')
const mem = new Areev('john.db', 'caller') // 3rd arg: passphrase for AES-256 at rest
await mem.addFact('john', 'prefers', 'tea', 0.95)
await mem.recall('john') // JSON string, newest-first
await mem.cal('RECALL facts WHERE subject = "john"')
await mem.memoryTool('{"command": "view", "path": "/memories"}') // Anthropic memory-tool backendEvery method returns a promise — store calls run on libuv's thread pool rather than blocking the event loop. The constructor is the exception, so opening a file still fails at the line that opened it. Await your writes: promises settle in completion order, not call order.
One memory = one file is the edge story. In stateless deployments (Cloud Run,
autoscaled containers) there is no durable disk — so the same store runs over
one PostgreSQL schema per memory instead, behind the non-default
postgres cargo feature — or, in practice, postgres-tls:
cargo install areev --features postgres-tls
areev add luis prefers window_seat --db 'postgres://user:pass@host/db?schema=memory_luis'
areev recall --db 'postgres://user:pass@host/db?schema=memory_luis' --subject luisWhich feature? postgres alone speaks plaintext and refuses a DSN that
asks for encryption (STO-E003) rather than silently downgrading it. Every
managed Postgres requires TLS — Azure Flexible Server, RDS with
rds.force_ssl, Cloud SQL — so postgres-tls, which honors the DSN's
sslmode and sslrootcert, is what a real deployment needs. Prefer
postgres only when the database is reachable over a trusted local socket.
No toolchain? AREEV_FLAVOR=postgres on the install script (above) fetches a
Linux binary with postgres-tls already compiled in.
The bindings ship with the backend built in — the same class takes a DSN where it takes a path:
m = areev.Areev("postgres://user:pass@host/db?schema=memory_luis")
areev.drop_postgres_schema(url, "memory_luis") # memory-level erasureconst m = new Areev('postgres://user:pass@host/db?schema=memory_luis')
dropPostgresSchema(url, 'memory_luis') // memory-level erasurelet mut m = Areev::open_postgres("postgres://user:pass@host/db", "memory_luis")?;Identical semantics by construction — the same store logic (fork election, supersession, op-log, BM25, hybrid recall) runs over either backend, pinned by a conformance suite that executes the same case list against both. The differences are deliberate and explicit:
- Latency class: point reads are microseconds embedded, milliseconds over a network. The voice frame path stays on the embedded backend by design.
- Multiple concurrent writers per memory: any number of app instances can
hold handles on the same schema. Write transactions claim their id blocks
from an in-schema counters row, which serializes them briefly — so the
op-log stays gapless and ordered for followers, racing supersedes of one
head produce one winner and one clean
SupersessionConflict, and readers never block (MVCC). One instance can likewise hold handles to many memories (the schema-per-tenant shape). - Vectors use pgvector; the
vector(dim)column is created when the first embedder is installed, and a dimension mismatch is a hard refusal rather than a degraded leg. - Erasure and portability map to schema operations:
pg_dump -n <schema>exports a memory,DROP SCHEMA … CASCADEerases one (exposed asdrop_postgres_schema). Recall telemetry rides the memory's schema too. Page-level crypto-erasure remains a file-backend capability; encrypt at the deployment layer (TDE/pgcrypto) instead. - Right to erasure and retention (both backends):
forget_subjecterases every structured reference to one identity — full history, object references, thread events, the dictionary entry itself — with replicating tombstones;forget_older_thanis the age-based retention sweep. Both are host-level operations, deliberately not reachable from CAL; see erasure.md for the scope contract and the documented OMS deviation. - HA is inherited: run it on a regionally-replicated Postgres and the memory inherits the failover, PITR, and backup story your ops team already drilled.
Deployment modes, auth, and what each mode may claim:
deployment-profile.md.
export AREEV_KEY="correct horse battery staple"
areev add --db secret.db --ns caller --subject john --relation prefers \
--object "window seat" --passphrase-env AREEV_KEY # AES-256-GCM, Argon2id keyThe passphrase-derived key covers the database and its CAS attachment sidecar;
destroying the key is crypto-erasure of the memory. Threat model and caveats:
security-model.md.
areev stream --db john.db --to s3-mounted/john/ # continuous op-log shipping (~Litestream, grain-level)
areev restore --db new.db --from s3-mounted/john/ [--until-hlc T] # incl. point-in-time
areev follow --db org-replica.db --from org-pub/ # subscribe: org knowledge → every edge
areev verify --db john.db # integrity + full content-address recheckOne memory = one file: the unit of erasure (crypto-erase = key destruction), sync, portability, and write parallelism. Partition by user, org, category, or conversation — your call.
- Build an agent that learns — and can unlearn, by hand: cookbook §10 (experience log → distilled lessons → proficiency chain → rollback).
- Turn on governed self-improvement:
areev init --db demo.db --template demo, thenareev loop run— the full guide isloop.md. - Import an existing corpus from mem0, Zep/Graphiti, Letta, LangMem, or
JSONL, with its edit history:
migrate.md. - See a real agent end to end: the invoice-to-accounting example runs keyless in CI, both chapters — the same agent in Python, TypeScript, and Rust, one file each, minting one content-addressed plan.