Product
What it stores, and what leaves your machine
A default install listens only on your machine and has no telemetry. It redacts recognized credentials before storing captured text, but filtering can miss sensitive information. Remote servers and optional cloud providers change where data goes.
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. A fresh install still mints a bearer token into an owner-only file, which the hooks and the CLI read, so nothing is open even on loopback.
No telemetry
The ai-memory binary has no analytics, crash reporting or usage tracking. Default local capture and search do not send project content to external model providers.
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
Stored data relies on filesystem permissions. Existing installs are not chmodded for you. Use disk encryption if you need protection when the machine is off.
Captured text is filtered before storage.
Hook payloads are untrusted text. They pass through a sanitizer that redacts recognized credentials and caps their size. It can miss sensitive information.
Hook payload
sanitize()
Secrets dropped
Stored
Size capped
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. The opt-in
[ingest] index_file_pathsadds one field: the checkout-relative path of the single file a kept file tool named, never its content, and an ignored path still sends nothing.
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.
- File paths and recognized shell arguments are checked lexically. Symlinks, shell variables and scripts read from files can evade matching; prompts are not path-filtered. This is best-effort capture filtering, not complete DLP.
- Assistant output is never stored by default. It takes a server flag and a client flag together.
- Backfill and managed-workstream imports apply the same capture rules to tool calls and results. Excluded calls are omitted; calls the policy cannot prove safe keep only tool-family metadata.
# .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
Match protection to your deployment
Remote access needs both authentication and TLS, including on a LAN. Apply every requirement that matches your setup before connecting clients.
One person, one machine
Loopback, a token in a file
ai-memory initcreates the token at<data_dir>/auth-tokens/defaultand the hooks, the CLI and the MCP bridge read it from there. An install from before 3.0 keeps running without auth until you runai-memory auth enable, which mints the token and re-registers every client.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.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.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.
What leaves the machine, and when.
These settings can send memory content to a model provider; they are off by default. Remote-server hooks, configured admission webhooks and your own backup destinations are separate data paths to review.
| 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 |
Your machine
Hooks
Wiki and index
Local embedder
Third parties
Wiki and index
Cloud embeddings
Cloud LLM
LLM rerank
Opt-in, off by default
Default local capture and search send no project content to a model provider. If you choose a cloud provider, review its terms and data handling. See the project’s data-handling notes.
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 logically deletes data from the selected project. History and earlier backups can retain copies. Separately, retention rules can remove unused session pages, and explicit expiry dates can remove other pages. Without --confirm, a purge only previews what it would remove.
# 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?
By default, ai-memory keeps captured project content on your machine. If you connect to a shared server, it sends that content there. Optional cloud embeddings or language-model features send data to the providers you configure. Your coding agent has its own data settings and provider connections.
Does the local embedder need the network?
On first start, the local embedder downloads about 87 MB of model files from Hugging Face and verifies their checksums. No project content is sent for that download. Set embedding_provider = "none" or supply 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. Project-level grants can restrict database-user accounts, but this remains a single-tenant service. Root has access to every project, and trusted-proxy deployments enforce access at the proxy. Use separate servers for teams that need independent isolation.
Try it on loopback first.
Start with a local, non-sensitive project. Review what is captured before connecting a shared server or a cloud provider.
