# ai-Memory Multi-User Authentication: Understanding the Bearer Token Ladder and DB User System

> Explore ai-memory's multi-user authentication: bearer tokens, DB users, and its four-rung ladder with fallbacks. Secure your AI applications effectively.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: deep-dive
- Published: 2026-08-23

---

**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`](https://github.com/akitaonrails/ai-memory/blob/main/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()` with `AuthLevel::Anonymous`
- **Source:** [`auth.rs#L5-L12`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs#L5-L12)

```bash

# 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_actor` with `AuthLevel::Root`
- **Source:** [`auth.rs#L29-L35`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs#L29-L35)

```bash
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>` matches `state.actor_proxy_bearer`
- Request includes `X-Memory-Actor-*` headers injected by the proxy

- **Resulting actor:** Built from proxy headers; `AuthLevel::Root` if asserting root identity, otherwise `AuthLevel::User`
- **Source:** [`auth.rs#L48-L73`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs#L48-L73)

```bash
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:
1. Extract bearer token from header
2. Hash with per-instance pepper via `hash_token()`
3. Lookup in `users.token_hash`
4. Populate `ActorContext` from `User` record

- **Resulting actor:** Username, display name, and email from database with `AuthLevel::User`
- **Source:** [`auth.rs#L76-L98`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs#L76-L98)

```bash

# 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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/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:

```http
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.

```bash

# 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_actor` and expected root token hash
- `actor_proxy_bearer` for trusted-proxy mode
- `multiuser` struct 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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/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`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs):

```rust
// 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 `ActorContext` and `AuthLevel` for 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).