Product
What it stores, and what leaves your machine
A default install listens on your machine only, strips secrets before anything is written, and has no telemetry. Every path that sends data out is off until you turn it on. This page also lists what the project does not defend against.
The default is local.
Install it, run it, and the only thing that can talk to it is your own machine.
Loopback only
The server binds 127.0.0.1:49374. No process outside the machine can reach it, which is why the default needs no password.
No telemetry
The binary has no analytics, crash reporting or usage tracking. Capture and search make zero network calls.
One directory you own
Wiki pages and the SQLite file live under one data directory. New directories are created 0700 and new files 0600 on Unix.
No encryption at rest
Version 1 relies on filesystem permissions. Existing installs are not chmodded for you. Use disk encryption if the laptop needs it.
Nothing is stored before it is sanitized.
Hook payloads are untrusted text. They reach the store through one function, and the type system enforces it.

One way in
The stored type can only be built by sanitize(). Code that skips the privacy strip does not compile.
Secrets are replaced
Matches become typed markers such as [REDACTED:github_token] or [REDACTED:jwt]. That covers secrets inside JSON and Windows credential paths, and terminal escape sequences are stripped. The strip is best effort.
Sizes are capped
Tool excerpts stop at 2 KB and prompts at 16 KiB. A 16 KiB backstop applies to every stored body after redaction.
Tool inputs are not kept
PreToolUse events never retain commands, arguments, paths or input bodies. Observations are an audit trail, never a full transcript.
Keep a path or a repository out
- A
[capture]block in.ai-memory.tomllists paths to ignore. Matching events are dropped on your machine, before the spool, the network and the logs. - Allowlist mode inverts it: a repository without the file emits no events at all.
- An invalid rule set falls back to metadata only. It never applies half a policy.
- Matching is lexical. Symlinks are not resolved, and prompts and shell commands cannot be tied to a path. This is a capture boundary. It is not complete DLP.
- Assistant output is never stored by default. It takes a server flag and a client flag together.
# .ai-memory.toml in the repository
[capture]
ignore_paths = ["private/**", "~/personal-notes/**"]
# Or capture only repositories that opt in
ai-memory install-hooks --apply --capture-mode allowlist
Add one rung at a time.
Each step exists for a specific situation. Stop at the one that matches yours.
One person, one machine
Loopback, no auth
The default. Nothing to configure, because nothing else can connect.
A homelab box or a second machine
Bearer token for the LAN
Create a token with
ai-memory generate-auth-tokenand setAI_MEMORY_AUTH_TOKEN. It is compared in constant time and protects/mcp,/hook,/handoffand the admin and API routes. SetAI_MEMORY_ALLOWED_HOSTSas well; it blocks DNS rebinding and does not replace the token.More than one person writes
Accounts and API keys
Passwords are stored as Argon2id hashes. Each machine gets an
aim_key that is printed once and never shown again.ai-memory api-key rotate <id>makes the old key return 401 at once. The first account turns every/admin/*route root only.Your team already has Keycloak, Okta or Entra ID
OIDC device auth for hooks
Native hooks and the CLI can carry a token from
ai-memory auth login oidc-device. The server does not validate OIDC tokens itself, so this needs an OIDC-aware gateway in front of it. It is hook and CLI auth through a gateway, and the server has no built-in SSO login.Anything beyond loopback
TLS through a reverse proxy
ai-memory does not terminate TLS, by design. Bearer tokens are not encrypted on the wire, so put Caddy, nginx or a Cloudflare Tunnel in front. The repo ships compose templates for Caddy and cloudflared. Set
AI_MEMORY_AUTH__SECURE_COOKIE=truewhen people sign in from other machines.
What leaves the machine, and when.
Four paths can send data out. All four are off by default.

| Path | What is sent | How it is turned on | Default |
|---|---|---|---|
| Remote embeddings | The full text of every stored page. Switching later also backfills the existing corpus. This path is not sanitized the way the others are. | embedding_provider = openai | voyage | google | openai-compat | Off |
| Assistant output capture | The assistant’s final-turn text, and only if you have also configured a cloud LLM. Global to the install, Claude Code and Codex only. | capture_assistant on the server and install-hooks --capture-assistant on the client | Off |
| LLM rerank | Each live query plus up to 30 bounded page titles and snippets. The query is not redacted, because redaction could change its meaning. | AI_MEMORY_RERANKER=llm | Off |
| LLM consolidation and lint | Prompts built from stored observations and pages go to whichever provider you chose. Point it at a local model and nothing leaves. | AI_MEMORY_LLM_PROVIDER | Off |
With none of these enabled there is no third party in the data path. Once you configure a cloud provider, that provider is your processor under your own agreement with it. DATA_HANDLING.md covers the GDPR framing. It is not legal advice.
Who wrote it, and how to remove it.
Attribution and audit
- With accounts on, each write carries its author. Pages record
author_idand alast_modified_byblock in the frontmatter. - Every mutation lands in an
audit_logtable, with the user’s identity on attributed rows. - The wiki is a git repository, so every page change is also a commit you can diff and restore.
- Admission webhooks receive the actor and can reject an operation.
- There is no audit log screen yet. The rows are in SQLite.
What a purge removes
Purge is a logical delete, scoped so that project A can never remove files from project B. Data has no expiry unless a page sets one.
# Remove one session
ai-memory purge-session --workspace default --project my-app --session-id <uuid> --confirm
# Remove a whole project
ai-memory purge-project --project experimental --confirm
| After a purge | Default | With --compact |
|---|---|---|
| Reachable through the API or MCP tools | No | No |
| Returned by search | No | No |
| Live markdown file in the wiki | No, best effort | No, best effort |
| Bytes still inside memory.sqlite | Yes, in free pages | No |
| Text still in the wiki git history | Yes | Yes |
| Present in backups taken earlier | Yes | Yes |
--compact is not forensic erasure. To remove text from history you rewrite the wiki’s git history and rotate your backups yourself. Lifecycle operations has the full procedure.
Where it stops.
Reporting a vulnerability
- Do not open a public issue. Open a private security advisory on GitHub.
- You get a response within 7 days. The target for a patch is 30 days, with credit in the changelog unless you decline.
- Only the latest release receives security fixes.
- Release assets ship with SHA-256 files, and CI actions are pinned to reviewed commits.
Questions and answers
Does ai-memory send my code or prompts anywhere?
No. There is no telemetry, and capture and search make no network calls. Data leaves the machine only if you turn on remote embeddings, assistant output capture with a cloud LLM, LLM rerank, or a cloud LLM provider for consolidation. All are off by default.
Does a default install make any network request?
One. Since 2.0 the default local embedder downloads an all-MiniLM-L6-v2 model of about 87 MB from Hugging Face on first start. No user data is sent. Set embedding_provider = "none" or place the model files yourself to avoid it.
Are secrets stored?
Every hook payload passes a sanitizer before storage. Recognized credentials are replaced with typed [REDACTED:kind] markers and bodies are size capped. The sanitizer is a best-effort credential strip and gives no guarantee.
Can I delete a session for good?
Purge is a logical delete. The content stops being reachable and searchable, but the text stays in the wiki git history and in earlier backups. The --compact flag clears the SQLite free pages and is not forensic erasure.
Is ai-memory multi-tenant?
No. It is a single-tenant service. Accounts give attribution, and every authenticated user can read every project on that server. Run separate servers for people who must not see each other’s data.
Try it on loopback first.
No account and no API key. Nothing can reach it but your own machine.