ai-Memory Multi-User Authentication: Understanding the Bearer Token Ladder and DB User System
ai-memory implements a four-rung authentication ladder that supports anonymous, root, trusted-proxy, and database-backed user modes, with graceful fallbacks to HTTP Basic auth for browser compatibility.
This Rust-based memory server for LLM tools uses a sophisticated authentication ladder to handle everything from single-user deployments to multi-tenant environments. The system, implemented in crates/ai-memory-mcp/src/auth.rs, progresses through four distinct rungs to determine the caller's identity and permission level.
The Four Authentication Rungs
Rung 0: Anonymous Mode (Auth Disabled)
When no Authorization header is present and the server started without a configured token, ai-memory falls back to completely open access.
- Resulting actor:
ActorContext::anonymous()withAuthLevel::Anonymous - Source:
auth.rs#L5-L12
# No token configured, no header sent — request succeeds as anonymous
curl http://localhost:49374/api/v1/summary
This zero-trust default enables rapid local testing without credential management overhead.
Rung 1: Root Bearer Token
The simplest protected mode uses a single root token supplied via the AI_MEMORY_ROOT_TOKEN environment variable. The middleware checks for exact match against Authorization: Bearer <token>.
- Resulting actor:
state.root_actorwithAuthLevel::Root - Source:
auth.rs#L29-L35
export AI_MEMORY_ROOT_TOKEN="sk-abc123..."
curl -H "Authorization: Bearer $AI_MEMORY_ROOT_TOKEN" \
http://localhost:49374/api/v1/summary
Root authentication grants unrestricted access to all endpoints and administrative functions.
Rung 1b: Trusted-Proxy Bearer Token
For deployments behind a reverse proxy that already authenticates users, ai-memory accepts a separate proxy token plus identity assertion headers. This enables multi-tenant scenarios where the infrastructure layer handles authentication.
Requirements:
-
Authorization: Bearer <proxy_token>matchesstate.actor_proxy_bearer -
Request includes
X-Memory-Actor-*headers injected by the proxy -
Resulting actor: Built from proxy headers;
AuthLevel::Rootif asserting root identity, otherwiseAuthLevel::User -
Source:
auth.rs#L48-L73
curl -H "Authorization: Bearer $AI_MEMORY_PROXY_TOKEN" \
-H "X-Memory-Actor-User: alice@example.com" \
-H "X-Memory-Actor-Name: Alice Smith" \
http://localhost:49374/api/v1/summary
Rung 2: Multi-User DB Token
Full multi-user mode activates when state.multiuser is Some, indicating a configured SQLite user database. Bearer tokens are validated against the users table using peppered hashing.
Authentication flow:
- Extract bearer token from header
- Hash with per-instance pepper via
hash_token() - Lookup in
users.token_hash - Populate
ActorContextfromUserrecord
- Resulting actor: Username, display name, and email from database with
AuthLevel::User - Source:
auth.rs#L76-L98
# Create a user (prints fresh 256-bit token)
ai-memory user add \
--username bob \
--name "Bob Builder" \
--email bob@example.com
# Use the printed token
curl -H "Authorization: Bearer sk-bob-token-xyz..." \
http://localhost:49374/api/v1/summary
Database User Structure
The User type in crates/ai-memory-core/src/user.rs defines the stored record:
| Field | Purpose |
|---|---|
username |
Unique identifier (plain text) |
name |
Display name for UI rendering |
email |
Contact identifier |
token_hash |
SHA-256 hash with per-instance pepper |
The pepper — a server-wide secret distinct from per-user salts — prevents rainbow table attacks even if the database is exfiltrated. The hash_token function in auth.rs combines this pepper with the presented token before database comparison.
Session Cookie Support
For browser compatibility, successful authentication via GET requests creates a long-lived ai_memory_session HTTP-only cookie. Subsequent requests skip the Authorization header check when this cookie is present.
- Source:
auth.rs#L77-L84
HTTP Basic Auth Fallback
When browsers request protected resources without prior authentication, ai-memory responds with 401 UNAUTHORIZED including a WWW-Authenticate header offering both challenges:
WWW-Authenticate: Basic realm="ai-memory", Bearer
Browsers prompt for credentials; the password field accepts any valid bearer token. The middleware extracts the password portion and processes it through the same ladder.
# Browser-style authentication (password is the bearer token)
curl -u ':<root-or-user-token>' http://localhost:49374/api/v1/summary
Configuration Architecture
The AuthState struct, instantiated at server startup, holds all authentication configuration:
root_actorand expected root token hashactor_proxy_bearerfor trusted-proxy modemultiuserstruct with SQLite handle, pepper, and writer for "last-seen" timestamps
Environment variables:
-
AI_MEMORY_ROOT_TOKEN— single root bearer token -
AI_MEMORY_PROXY_BEARER— trusted proxy bearer token -
MULTIUSER_PEPPER— hashing secret for DB tokens -
Source:
auth.rs#L86-L104
Implementing Custom Authentication
To extend the ladder or integrate with external identity providers, modify crates/ai-memory-mcp/src/auth.rs:
// Example: Adding a rung for OAuth2 token introspection
async fn authenticate_oauth(state: &AuthState, token: &str) -> Option<ActorContext> {
// Call external IdP, map to ActorContext
}
Insert new rungs between existing ones by returning early with the appropriate ActorContext and AuthLevel on successful validation.
Summary
- Anonymous mode requires zero configuration and enables immediate local testing
- Root token mode protects single-user deployments with a single environment variable
- Trusted-proxy mode delegates authentication to infrastructure for multi-tenant setups
- DB user mode provides full multi-user support with salted, peppered token hashing
- Session cookies and HTTP Basic auth ensure browser compatibility without compromising API design
- All authentication paths converge on
ActorContextandAuthLevelfor consistent authorization downstream
Frequently Asked Questions
How do I migrate from root-only to multi-user mode?
Start the server with AI_MEMORY_ROOT_TOKEN configured and MULTIUSER_PEPPER set, then use ai-memory user add to create initial users. Existing root authentication continues working; gradually transition clients to individual user tokens. The root token remains useful for administrative operations.
Why does ai-memory use a pepper instead of per-user salts?
The per-instance pepper protects against database-only compromise scenarios. Combined with the natural randomness of 256-bit bearer tokens, this provides sufficient security without requiring per-user salt storage. Rotating the pepper requires regenerating all user tokens.
Can I disable authentication entirely in production?
Yes — omit AI_MEMORY_ROOT_TOKEN and AI_MEMORY_PROXY_BEARER and don't configure multi-user mode. However, this exposes all memory data to any network client. The anonymous mode exists primarily for development and containerized single-user deployments behind existing network boundaries.
What happens when multiple authentication methods are present?
The ladder evaluates rungs in order: anonymous, root, proxy, then database. The first successful match determines the actor. A request with both valid root token and valid user token authenticates as root (rung 1 precedes rung 2).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →