AI News HubLIVE
In-site rewrite5 min read

AegisDB – self-hosted memory for AI agents, in one C binary

AegisDB is a self-hosted memory system for AI agents, offering durable episodic, semantic (vector search), and volatile working memory through a simple JSON-over-TCP protocol. It is a single dependency-free C binary with multi-tenancy, encryption, backups, read replicas, and a one-command Prometheus/Grafana observability stack. Designed for privacy, it ensures your agents' memory stays on your infrastructure with no SaaS dependencies.

SourceHacker News AIAuthor: d4n-larsson

Notifications You must be signed in to change notification settings

Fork 0

Star 0

BranchesTags

Open more actions menu

Folders and files

NameName

Last commit message

Last commit date

Latest commit

History

260 Commits

260 Commits

.claude

.claude

.github/workflows

.github/workflows

bench

bench

docs

docs

include/aegisdb

include/aegisdb

integrations

integrations

scripts

scripts

site

site

src

src

tests

tests

third_party

third_party

.dockerignore

.dockerignore

.env.example

.env.example

.gitignore

.gitignore

CLAUDE.md

CLAUDE.md

CMakeLists.txt

CMakeLists.txt

Dockerfile

Dockerfile

LAUNCH.md

LAUNCH.md

LICENSE

LICENSE

Makefile

Makefile

README.md

README.md

docker-compose.yml

docker-compose.yml

Repository files navigation

Self-hosted memory for your AI agents. One small C binary — multi-tenant, encrypted, with backups, read replicas, and a one-command Prometheus + Grafana stack. Your agents' memory stays on your box; nothing ships to a SaaS.

AI agents forget everything between sessions. AegisDB gives them durable, searchable long-term memory — episodic history, semantic facts with vector search, and volatile working memory — behind a dead-simple JSON-over-TCP protocol, with a first-class Claude Code integration. It's a single dependency-free binary you run yourself: your data, your box, no third party in the loop.

Quickstart

Run the server — no clone, no toolchain (prebuilt multi-arch image on GHCR):

docker run -d --name aegisdb -p 9470:9470 -v aegis-data:/data \ ghcr.io/d4n-larsson/aegisdb:latest

Talk to it — the same binary is also the client:

docker exec aegisdb aegisdb client ping docker exec aegisdb aegisdb client put --type semantic --tags user "prefers dark mode" docker exec aegisdb aegisdb client search --tags user --top-k 5

Want the whole observability stack (server + Prometheus + a pre-built Grafana dashboard) in one command? Clone this repo and:

docker compose --profile monitoring up # dashboard on http://127.0.0.1:3000

Giving Claude Code a persistent memory is a one-liner (with a server running): uvx --from aegisdb-mcp aegisdb-init — see Use as Claude Code memory.

Why AegisDB

Self-hosted & private. Your agents' memory never leaves your infrastructure — no SaaS, no per-token billing, no data-sharing. Encrypt it at rest with one flag.

One binary, no dependencies. Written in C; the only vendored code is cJSON and the crypto. No JVM, no Python runtime, no external database to babysit.

Built for teams. Multi-tenant auth (per-namespace, scoped tokens), per-tenant quotas + rate limits, online backups, read replicas, and turnkey Prometheus/Grafana observability.

Claude Code native. Ships an MCP server + hooks so Claude remembers across sessions — installable with a single command.

Production-minded. Corruption-resilient append-only log, crash recovery, a documented security review, and CI that runs ASan/UBSan/TSan plus continuous fuzzing.

Features

Durable episodic memory — append-only log with magic + CRC32 framing, corruption-resilient recovery, and legacy-log migration

Semantic facts — updateable records (latest version wins)

Working memory — volatile per-session ring buffer with TTL and promotion

Retrieval — lookup by ID, time-range search, tag search (all/any), semantic (embedding) search ranked by cosine similarity weighted by importance × confidence; count and consolidate (dedup) over the same filters

Semantic scale — exact cosine while small; past --ann-threshold an HNSW graph for sublinear approximate top-K, built off the write path and sharded so the build parallelizes (--ann-shard-target), optionally int8-quantized

Relationships — directed edges between records, graph traversal, and agent-namespace isolation

Multi-tenant auth — optional bearer tokens (constant-time check; ping exempt), each bound to a namespace + scope (ro/rw/admin) so one server safely isolates many tenants

Per-tenant limits — optional storage quotas (records/bytes) and a request rate limit per namespace, so one team member's runaway agent can't fill the disk or monopolize the shared server

Encryption at rest — optional XChaCha20-Poly1305 (vendored, no crypto dependency) over the log + checkpoints; opt-in via --encryption-key-file, with an offline migrator and encrypted backups/replicas

Observability — stats op plus a drop-in Prometheus exporter + Grafana dashboard (docker compose --profile monitoring up)

Operations — online snapshot/restore backups and read replicas

Concurrency — sharded poll() event-loop threads (--io-threads); selectable fsync durability (sync / batch / interval)

Requirements

Linux (primary target) with GCC 11+ or Clang 14+

One of: CMake 3.20+ or GNU Make

Python 3.8+ (optional, for the example client below)

Build

With CMake (canonical)

cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build ctest --test-dir build --output-on-failure # runs the unit test suite

With Make (no CMake required)

make # builds build/aegisdb make test # builds and runs the C unit tests make integration # wire-protocol contract tests (launches the server) make check # unit + integration make clean

The server binary is produced at build/aegisdb.

With Docker

Prebuilt multi-arch images (linux/amd64, linux/arm64) are published to GitHub Container Registry on every push to main and every release tag — no clone or toolchain needed:

docker run -p 9470:9470 -v aegis-data:/data ghcr.io/d4n-larsson/aegisdb:latest

or pin a release: ghcr.io/d4n-larsson/aegisdb:0.1.0

To build it yourself instead, a multi-stage Dockerfile (Debian-slim) compiles the server and ships a minimal runtime image. Data persists in a named volume at /data.

Build and run with Docker Compose

docker compose up --build # serves on localhost:9470

Or build and run the image directly

docker build -t aegisdb . docker run -p 127.0.0.1:9470:9470 -v aegis-data:/data aegisdb

Compose is configured by an optional .env file — copy the template and edit:

cp .env.example .env # then tweak port, durability, tenant limits, … docker compose up --build

Every setting has a default, so .env is optional. It exposes the common flags as named vars (AEGIS_PORT, AEGIS_EMBEDDING_DIM, AEGIS_DURABILITY, AEGIS_TENANT_MAX_RECORDS, …) plus AEGIS_EXTRA_ARGS for anything else (--auth-token-file, --io-threads, ANN tuning, …). See .env.example for the full list.

To skip building, point docker-compose.yml at the published image: replace build: . with image: ghcr.io/d4n-larsson/aegisdb:latest.

The image ships a HEALTHCHECK that uses the binary's built-in --health-check probe (no extra tooling in the image), so docker ps and Compose depends_on: condition: service_healthy reflect real server liveness.

The container runs as an unprivileged user. The server listens on 0.0.0.0:9470 inside the container, but Compose publishes that port on the host's loopback (127.0.0.1) only by default — because the wire protocol is unauthenticated and plaintext out of the box, it must not be reachable off-box until you secure it. To expose it deliberately, set AEGIS_BIND=0.0.0.0 (or a specific host IP) in .env, and first enable authentication: mount a token file into /data and add --auth-token-file /data/tokens.txt (to AEGIS_EXTRA_ARGS under Compose; see Authentication). Even with auth, tokens travel in plaintext, so terminate TLS at a trusted proxy for any non-loopback exposure. Override other flags by appending them to the run command, e.g. docker run aegisdb --embedding-dim 1024, or (with Compose) via .env.

Run

./build/aegisdb --data-dir ./data --port 9470

Expected startup output:

2026-06-28 12:00:00.000 INFO [aegisdb] AegisDB 0.1.0 starting (log level: info) 2026-06-28 12:00:00.000 WARN [aegisdb] no auth tokens configured; ... 2026-06-28 12:00:00.000 INFO [aegisdb] recovery complete: N records loaded 2026-06-28 12:00:00.000 INFO [aegisdb] listening on 0.0.0.0:9470 2026-06-28 12:00:00.000 INFO [aegisdb] data directory: ./data

Logs go to stderr as [aegisdb] . Control the verbosity with --log-level error|warn|info|debug (default info) or the AEGISDB_LOG_LEVEL environment variable — the flag takes precedence. At debug, the server logs every accepted connection and dispatched operation.

The WARN line appears only when the server is started without --auth-token/--auth-token-file (see Authentication).

Talk to it

The same binary is also a client — no nc, no hand-written JSON:

aegisdb client ping aegisdb client put --type semantic --tags user "prefers dark mode" aegisdb client get 1 aegisdb client search --tags user --top-k 5 aegisdb client stats

Host, port, and token default to $AEGIS_HOST / $AEGIS_PORT / $AEGIS_TOKEN (127.0.0.1 / 9470 / none) or --host/--port/--token. The exit code is 0 on an ok response, so it scripts cleanly. Inside Docker: docker exec aegisdb aegisdb client stats.

To create a tenant token, gen-token prints a ready token-file line (hashed) and the one-time plaintext token:

$ aegisdb gen-token --namespace acme --scope rw sha256$… acme rw # paste into your --auth-token-file token: 9f3c… # give to the client (AEGIS_TOKEN); not recoverable

Configuration flags

Flag Default Description

--data-dir ./data Persistence directory

--port 9470 TCP listen port

--phase 4 Highest enabled feature phase (gates operations)

--io-threads 2× CPUs (8–64) poll() event-loop threads for dispatch parallelism (does not cap concurrent connections). Alias: --workers

--max-payload 1048576 Max data size (1 MiB)

--embedding-dim 384 Expected embedding vector length

--ann-threshold 10000 Live vectors before semantic search switches from exact scan to the HNSW graph

--ann-ef-search HNSW default HNSW query beam width (recall/latency knob)

--ann-shard-target 25000 Target vectors per HNSW shard; the graph splits into ~count/n shards (capped by CPUs) so the build parallelizes

--ann-quantize off Store HNSW vectors as int8 (~4× less memory, small recall cost)

--durability interval sync (fsync per write), batch (per --fsync-batch records), or interval (per --fsync-interval-ms)

--fsync-batch 1000 Records between fsync calls in batch mode

--fsync-interval-ms 1000 Flush cadence in interval mode (floored at the ~1s maintenance tick)

--checkpoint-sec 60 Index checkpoint cadence so recovery replays only the tail; 0 disables

--compact-sec 300 Log-compaction check cadence; compacts only when enough of the log is dead; 0 disables

--tenant-max-records 0 Per-namespace live-record cap (0 = unlimited); enforced only when auth is enabled

--tenant-max-bytes 0 Per-namespace live-byte cap (0 = unlimited)

--tenant-rate-qps 0 Per-namespace request rate limit in req/s, burst = 1s (0 = unlimited)

--max-index-bytes 0 Soft cap on in-RAM index size; inserts return MEMORY_LIMIT past it so a growing dataset backpressures instead of getting OOM-killed (accepts K/M/G; 0 = unlimited). Watch stats.memory.

--replication-port — Serve the read-replica log stream on this port (primary; requires --replication-token)

--replication-token — Token to subscribe to / follow the replication stream

--replicate-from — Follow this primary's replication port as a read-only replica (implies --read-only)

--read-only off Refuse client writes (READ_ONLY)

--working-capacity 256 Working-memory ring buffer size

--restore — One-shot: install the snapshot at into an empty --data-dir, then exit

--log-level info error, warn, info, or debug (also $AEGISDB_LOG_LEVEL)

--auth-token — Accept this global admin token (repeatable)

--auth-token-file — Accept tokens, one per line: [namespace] [ro|rw|admin]; a token may be sha256$ (hashed at rest)

--hash-token

Print the token's sha256$ form and exit (paste into the token file)

--encryption-key-file — Encrypt the log + checkpoints at rest with the 32-byte key (64 hex chars) in (Encryption at rest)

--encrypt-migrate

Rewrite -

[truncated for AI cost control]