Four crypto surfaces, every one named, every one with the algorithm and the threat model. SQLCipher AES-256 at rest. mTLS for peer-to-peer federation. HMAC-SHA256 for webhook integrity. Signed git tags + SBOM for supply chain. What's covered, what isn't, and what's coming in v0.7.
The whole SQLite database file is encrypted with the operator's passphrase. No plaintext on disk. Failed key triggers an error at db::open: "PRAGMA key failed (wrong passphrase or unencrypted DB?)".
Peer-to-peer federation traffic travels over mutual TLS. Each peer's client cert is pinned by SHA-256 fingerprint in the allowlist file — only allowlisted peers can push or pull, even if they have a valid cert.
Every outbound webhook POST carries an X-AI-Memory-Signature: sha256=… header. Body + timestamp are HMAC'd with the subscription's secret. Receivers verify before trusting the payload.
Every release tag is GPG-signed. CI runs cargo audit against the RustSec advisory DB. SBOMs travel with each binary. Build is reproducible from the signed tag — no opaque inputs.
PRAGMA key = '…' with the operator's passphrase. SQLCipher's libsqlcipher (drop-in libsqlite3 replacement) handles AES-256-CBC of every page + PBKDF2-HMAC-SHA512 key derivation. A wrong key → loud error at startup.The passphrase has to live somewhere — usually a secret manager (Vault, AWS Secrets Manager, GCP Secret Manager) or a systemd credential. SQLCipher protects the data file; it doesn't protect the running process's memory or the passphrase environment variable. Pair with disk encryption and process hardening for defense in depth.
VACUUM INTO, sqlite3 .backup) produces an already-encrypted output. Backup tooling doesn't need to know about encryption — the bytes on disk are already opaque.rustls — Rust-native TLS, no OpenSSL dependency. TLS 1.3 only (no fallback to 1.2 or below). Forward secrecy via X25519 ECDHE. AEAD ciphers only.9eeb453 for v0.6.3 final.mTLS authenticates the peer node, not the memory author. Once a peer is allowlisted, every memory it pushes is trusted. v0.7 Bucket 1 (Ed25519 attested identity) closes that gap by attaching a per-memory signature so individual memories' provenance is verifiable independent of which peer relayed them.
secret_hash in the subscriptions table). Every dispatch is signed with the secret using HMAC-SHA256.{timestamp}.{body}. Receivers reconstruct the canonical string and HMAC-SHA256 it with their stored secret; X-AI-Memory-Signature: sha256=… must match. Timestamp is included so receivers can reject replays older than a window of their choice.git tag -v v0.6.3 verifies. CI workflow refuses to publish releases from unsigned tags.cargo-audit and runs it against the RustSec advisory DB on the Ubuntu job. A new CVE in any transitive dep fails CI before merge. Adversarial dep updates can't slip in.v0.6.3 ships a placeholder signature column on memory_links + a deferred observed_by column. v0.7 Bucket 1 fills that contract.
sqlite3 .backup produces an encrypted file. But if you export to JSON via memory_export, the export is plaintext — wrap it in your own encryption before storing.