marker-file.md gains a section on the `server` key: registering profiles, the fail-closed rules, inheritance and the walk boundary with the allowlist-mode caveat that comes with it, check-capture output, which integrations route or drop, what `uninstall` removes, that two profiles may share a URL with different tokens (one server, several identities under #708), and two known limits (older binaries draining a shared spool; MCP and `ai-memory run` still using the install server). install.md, cookbook.md, security.md and the README point to it, security-boundaries.md adds row 11d, and CHANGELOG records the feature and the backfill fix; the Kimi `{}` fix reached release/2.5 through the cherry-pick to main (#996), so its commit is no longer part of this branch.
6.1 KiB
Security
The full security model. Moved verbatim from the README front page; the README keeps a summary.
Loopback-only (127.0.0.1:49374) with no auth is the default because
it is safe for a single-user laptop: no process outside the machine can
reach the server.
Unauthenticated non-loopback HTTP now fails closed. Set
AI_MEMORY_AUTH_TOKEN or bind loopback; --allow-insecure-no-auth is an
intentional, dangerous exception for plain HTTP only. Authentication does not
encrypt bearer tokens: for LAN or remote access, use the ready
Caddy or
Cloudflare Tunnel templates described in
the HTTPS reverse-proxy guide.
Enable bearer auth when the server is exposed beyond loopback, when untrusted local processes share the machine, or when the data dir holds sensitive project history:
TOKEN=$(ai-memory generate-auth-token)
docker run -d --name ai-memory \
--restart unless-stopped \
-p 0.0.0.0:49374:49374 \
-v ai-memory-data:/data \
-e AI_MEMORY_AUTH_TOKEN="$TOKEN" \
-e AI_MEMORY_ALLOWED_HOSTS="<server-ip>,localhost,127.0.0.1" \
akitaonrails/ai-memory:latest
ai-memory install-mcp --client claude-code --apply \
--server-url "http://<server-ip>:49374/mcp" --auth-token "$TOKEN"
ai-memory install-hooks --agent claude-code --apply \
--server-url "http://<server-ip>:49374" --auth-token "$TOKEN"
When users must not see each other's projects at all, run separate servers.
One machine can still capture for several of them: register each server as
a named profile with ai-memory server add and let each repository's
.ai-memory.toml select one with server = "<name>". The marker never holds
a URL or token, a profile can be restricted to the directories it serves, and
a selection that does not resolve drops the event instead of sending it to
another server. See
marker-file.md.
Bearer auth protects /mcp, /hook, /handoff, /workstream/*, and
machine calls to /admin/* and /api/v1/*. Humans sign in at
POST /auth/login; the console uses an HttpOnly session cookie plus CSRF,
not a Bearer in localStorage. Custom SPA HTML at /web is public static.
When human auth listens beyond loopback,
AI_MEMORY_AUTH__SECURE_COOKIE=true is required and signals that a trusted
HTTPS reverse proxy owns the browser-facing edge. It makes the session cookie
HTTPS-only. Close or redirect direct HTTP access to that hostname.
Non-loopback binds should also set AI_MEMORY_ALLOWED_HOSTS to guard against
DNS rebinding.
Busy shared hook servers can also set AI_MEMORY_HOOK_RATE_PER_SEC (tokens per
second per actor/session source) and optionally AI_MEMORY_HOOK_RATE_BURST to
bound one runaway session without blocking unrelated hook sources. Unset or 0
rate leaves the limiter disabled.
For shared servers where each developer should authenticate their own hook writes, native Claude Code hooks can use a stored OIDC device token instead of embedding a shared static token:
ai-memory auth login oidc-device \
--issuer "https://issuer.example.com/realms/team" \
--client-id "ai-memory-cli"
ai-memory install-hooks --agent claude-code --apply \
--server-url "http://<server-ip>:49374"
OIDC hook auth requires the native ai-memory hook ... command path. The Docker
wrapper keeps shell-script hooks by default; set up OIDC from a native release
binary or source install. Thin-client HTTP commands such as ai-memory status
and ai-memory search also use the stored OIDC access token when no static
AI_MEMORY_AUTH_TOKEN / [auth].bearer_token is configured; the static bearer
still wins when present. This is for OIDC-aware gateways/bridges; native
ai-memory server auth still accepts static root bearer / DB-user tokens, and
/admin/* remains root-only unless a gateway translates accepted OIDC auth into
upstream auth that ai-memory accepts.
OIDC/Keycloak session ids are login-provider sessions, not ai-memory agent
sessions. Shared servers that rely on [auto_scope] session isolation still
need explicit workspace + project / scopes, or a bridge that forwards the
real lifecycle-hook session id on MCP requests.
Want HTTPS? ai-memory deliberately does not terminate TLS itself —
the right answer is a battle-tested reverse proxy in front of it.
docs/https-via-proxy.md is the deployment
guide, with copy-paste docker compose templates in
docker/compose.tls.caddy.yml (Caddy
with Let's Encrypt or internal CA) and
docker/compose.tls.cloudflared.yml
(Cloudflare Tunnel — no open ports). Both are recommended once you
turn on multi-user or bind beyond loopback. The Quick Start happy
path of single-user on loopback doesn't need TLS — that case is
called out explicitly in the guide so you don't add ceremony where
it doesn't earn its keep.
Multi-user attribution (v0.8, optional) plus human login. When more
than one human shares a server, ai-memory attributes each write to a
named user. Humans sign in with username/password; agents and CLIs use
Authorization: Bearer (AI_MEMORY_AUTH_TOKEN for root automation, or
an aim_ key from ai-memory api-key add). Data stays single-tenant —
there is no per-page RBAC. A
[auth].token_pepper is required for DB-user authentication, but creating the
first user row is what immediately switches every /admin/* endpoint to
root-only, including status/search/read-page and user-management routes.
ai-memory init generates a pepper for new installs without changing
single-user behavior until a user is added. An SSO gateway can instead use a
dedicated [auth].actor_proxy_bearer_token and trusted X-Memory-Actor-*
headers; its credential is deliberately separate from the root bearer so a
missing identity cannot become root. See
docs/users.md for the full walkthrough and the
four-rung auth ladder.
See docs/deploy.md for the full homelab pattern
with bearer auth, host allowlisting, and TLS/reverse-proxy options.