ai-memory v0.8.0

ai-memory Security Overview

Threat model, trust boundaries, and hardening options for operators.

For responsible disclosure: security@alphaone.dev. Please encrypt against the maintainer key listed in SECURITY.md.sig (if present) or via the fingerprint on our releases page.

Threat model

ai-memory is designed to be safe under the following attacker capabilities:

  1. Local untrusted user on the same machine as the CLI or HTTP daemon. They should NOT be able to read memories outside their own database, alter governance state, or escalate to the daemon’s effective UID.
  2. Network attacker reaching the HTTP daemon. They should NOT be able to bypass API-key / mTLS, inject memories with a forged agent_id, or enumerate memories without authorization.
  3. Compromised peer holding valid mTLS cert. Under the default posture they can author only as themselves: the peer-attestation layer re-stamps inbound rows with the authenticated peer identity, and a body.sender_agent_id claim not on that peer’s operator-configured allowlist is refused with sender_agent_id_mismatch (see docs/federation.md Layer 3). v0.8.0 (#1464) extends this from the body sender to per-memory granularity: each synced row’s claimed metadata.agent_id is checked against the peer’s authorship allowlist, and an unauthorized relayed claim is rewritten to the sender and stamped attest_level = "claimed" so a forged claim can never own the row or charge another agent’s quota (resolve_inbound_attribution). The legacy trust-the-body posture exists only behind the explicit AI_MEMORY_FED_TRUST_BODY_AGENT_ID=1 escape hatch. Operators should still treat the agent_id on synced memories as a claimed (allowlist-authorized) identity, not a cryptographically attested one, and keep the mTLS peer allowlist tight. (Store-path agent attestation — #626 Layer-3 — upgrades directly authored CLI/MCP/HTTP writes to agent_attested when a valid Ed25519 signature is presented; the store-path default will flip to require-attestation in v0.9 (#1751), preceded by a v0.8.0 deprecation WARN. The federation receive path remains claimed-by-default — per-write cryptographic attestation of synced memories needs a wire-protocol extension (sender signs each row, receiver verifies) and is tracked under Pillar-3 #1719; mTLS + the per-memory authorship allowlist stay the trust boundary for synced writes.)
  4. Compromised LLM (Ollama returning malicious content). Autonomy hooks never exec or write to disk outside the database. Worst case: bad tags, spurious contradiction flags. Reversible via the rollback log.

Out of scope (non-goals):

Trust boundaries

┌──────────────┐   no auth           ┌──────────────┐
│ MCP client   │───────────────────▶│ ai-memory    │
│ (Claude Code)│  stdio JSON-RPC     │ daemon /     │
├──────────────┤                     │ MCP server / │
│ HTTP client  │────────────────────▶│ CLI          │
│ (SDK, curl)  │  API key + mTLS     │              │
├──────────────┤   mTLS + sync       └──────┬───────┘
│ peer daemon  │◀────────────────────┐      │
└──────────────┘                     │      │ SQLite mutex
                                     │      ▼
                              ┌──────┴──────┐
                              │ ai-memory.db│
                              │ (optionally │
                              │  SQLCipher) │
                              └─────────────┘

Authentication

API key (HTTP)

Set at daemon startup:

ai-memory serve --api-key "$(pwgen -s 48 1)"

Or via config file:

api_key = "long-random-string"

Every HTTP endpoint except /api/v1/health enforces the key (when the mTLS allowlist is enforced, the /api/v1/sync/* federation endpoints additionally bypass the key check — they have already cleared a stronger transport gate; see #702 and docs/federation.md). Accepts either:

Rotation: generate new key, update config, restart the daemon. Clients have a grace period determined by their connection lifetime — there’s no in-flight rotation today.

mTLS (Layer 1 + Layer 2)

Layer 1 enables HTTPS:

ai-memory serve \
  --tls-cert /etc/ai-memory/cert.pem \
  --tls-key  /etc/ai-memory/key.pem

rustls under the hood, no OpenSSL dep. PKCS#8 and RSA keys both supported. Certificate expiry is the operator’s responsibility; the daemon does not notify on impending expiry.

Layer 2 adds a client-cert fingerprint allowlist:

ai-memory serve \
  --tls-cert /etc/ai-memory/cert.pem \
  --tls-key  /etc/ai-memory/key.pem \
  --mtls-allowlist /etc/ai-memory/peer-fingerprints.txt

Allowlist format: one SHA-256 hex fingerprint per line, optional : separators, # comments. Any peer not on the allowlist cannot complete the TLS handshake.

# peer-a.example.com
2F:79:84:AB:…:CD
# peer-b.example.com
7E:1B:FE:22:…:AA

Data at rest

SQLCipher encryption

Opt-in cargo feature. Replaces the bundled SQLite with SQLCipher (AES-256 page encryption).

cargo build --release --no-default-features --features sqlcipher

Supply the passphrase via a root-readable file:

echo -n 'strong-passphrase' > /etc/ai-memory/db.key
chmod 0400 /etc/ai-memory/db.key
ai-memory --db-passphrase-file /etc/ai-memory/db.key <cmd>

The CLI reads the file, exports AI_MEMORY_DB_PASSPHRASE for the process lifetime, and clears it on exit. Passphrase never appears in ps//proc/<pid>/environ.

Defaults (page size, cipher, KDF iterations) match SQLCipher 4.x. To open the DB manually: sqlcipher ai-memory.db + PRAGMA key='…';.

File permissions

The daemon expects the DB file + WAL/SHM companions to be writable only by the ai-memory user:

chown ai-memory:ai-memory /var/lib/ai-memory/*.db*
chmod 0600 /var/lib/ai-memory/*.db*

The bundled systemd unit enforces ReadWritePaths=/var/lib/ai-memory and drops all capabilities.

Backups

ai-memory backup writes SQLCipher-encrypted snapshots too when the daemon is built with --features sqlcipher. The sha256 manifest commits to the ciphertext, not the plaintext — verification works without the passphrase.

Input validation

Every write path validates:

Body-size limit: 2 MiB per request (DefaultBodyLimit::max(HTTP_BODY_LIMIT_BYTES), HTTP_BODY_LIMIT_BYTES = 2 * MIB in src/lib.rs).

Network hardening

Bind address

ai-memory serve --host 127.0.0.1   # loopback-only (default)
ai-memory serve --host 0.0.0.0     # public (requires TLS + auth)
ai-memory serve --host 10.0.0.5    # specific interface

Never bind 0.0.0.0 without TLS + API key + mTLS.

Two related hardening knobs:

Webhooks (SSRF-hardened)

The webhook dispatch path validates URLs before POSTing:

Subscribers can still receive malicious webhook-URL registrations — review subscription inserts through governance if that’s a concern.

Sync peer push

POST /api/v1/sync/push is gated by the mTLS fingerprint allowlist at the transport layer, and at v0.7.0 by per-message Ed25519 signatures by default: AI_MEMORY_FED_REQUIRE_SIG=1 (#791 secure default) rejects missing/invalid X-Memory-Sig headers with 401, and AI_MEMORY_FED_REQUIRE_NONCE=1 (#922 secure default) refuses byte-for-byte replays via per-peer nonce freshness (X-Memory-Nonce; nonces persist across restarts in the federation_nonce_cache table, schema v51 / #1255). Peer attestation (AI_MEMORY_FED_PEER_ATTESTATION) further scopes what an authenticated peer may claim and pull. Never run the sync endpoint on a public network without mTLS.

Governance

Policies are set per-namespace via memory_namespace_set_standard (MCP) or directly in SQL. The per-action levels (crate::models::GovernanceLevel) are:

An action that hits an approve policy returns 202 Accepted with a pending_id; approvers POST to /api/v1/pending/{id}/approve.

Critical: consensus: N requires pre-registered agents (issue #216 / #234). Unregistered approvers cannot satisfy the quorum. See ai-memory agents register.

Audit trail

Every memory carries metadata.agent_id (immutable once written). Governance actions log decided_by. The curator’s rollback log preserves every autonomous action as a reversible snapshot memory in _curator/rollback/<ts>.

For compliance-grade audit, also:

Responsible disclosure

If you find a security vulnerability, please:

  1. Do not open a public issue.
  2. Email security@alphaone.dev with details. Encrypt to the key listed on our releases page if the impact is severe.
  3. We aim to acknowledge within 72 hours and ship a fix within 14 days for critical issues. We’ll credit reporters who wish to be credited.

Our bug bounty program is documented at https://alphaonedev.github.io/ai-memory-mcp/security.