# ai-memory Security Model and Authentication Methods: The Layered Rung System

> Explore the ai-memory security model and authentication methods. Discover its six-rung system, scaling from loopback to OIDC tokens for diverse deployments.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: architecture
- Published: 2026-09-10

---

**ai-memory implements a six-rung security model that ranges from unauthenticated loopback-only access to OIDC device tokens, allowing deployments to scale from single-user laptops to multi-user TLS-protected servers.**

The ai-memory security model separates machine-only access from human interaction through a series of progressively stronger authentication rungs. As implemented in akitaonrails/ai-memory, each rung maps to a specific credential type and trust boundary, controlled by environment variables and middleware in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs). This design lets the same binary run securely on a shared server or insecurely on a disconnected developer laptop.

## The Six Authentication Rungs

ai-memory organizes identity into six distinct levels, or rungs, that determine how an incoming request is validated and what privileges it receives.

### Rung 0 — No Authentication (Loopback-Only)

By default, ai-memory deploys in single-user laptop mode with **no authentication** required. When `AuthState::enabled()` returns false, the `require_bearer` middleware becomes a no-op and injects an anonymous `ActorContext`, accepting any request on `127.0.0.1` without credentials. This behavior is defined in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) at lines 98–108.

### Rung 1 — Root Bearer Token

The first active rung uses a **static bearer token** supplied via the `AI_MEMORY_AUTH_TOKEN` environment variable or generated with the `ai-memory generate-auth-token` command. The middleware compares the incoming bearer to the configured value using `subtle::ConstantTimeEq` to prevent timing attacks, and on success stamps the request with the root actor template (`AuthLevel::Root`). You can see the comparison logic in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) at lines 14–20.

### Rung 2 — Multi-User Database Tokens

For multi-user servers, ai-memory supports **per-user API keys** stored in the `api_credentials` table and hashed with a per-server pepper. When a bearer token does not match the root token, the server hashes it via `hash_token` and looks it up through `MultiUserResolver`. A successful hit injects the corresponding `User` and assigns `AuthLevel::User`, as implemented in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) at lines 53–84.

### Rung 3 — Trusted Proxy Bearer

Organizations that terminate OIDC authentication at a reverse proxy can use a **dedicated proxy bearer token** (`actor_proxy_bearer`). If the incoming bearer matches the proxy token, the server parses `X-Memory-Actor-*` headers describing the real human identity through the `trusted_proxy_actor` function and creates a separate `ActorContext`. The resulting level is `Root` only when the asserted identity equals the configured root identity; otherwise it is `User`. This logic spans [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) at lines 22–33, 46–54, and 118–140.

### Rung 4 — Human Password and Session

Interactive browser access is protected by **username and password authentication** with Argon2-hashed credentials and short-lived HttpOnly session cookies (`ai_memory_session`). The `human_auth` module enforces login throttling, CSRF checks, and session handling. Sessions intentionally never authenticate `/mcp`, `/hook`, or `/workstream` endpoints; they protect only browser routes under `/web`. Refer to [`crates/ai-memory-mcp/src/human_auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/human_auth.rs) at lines 1–6 and lines 29–38.

### Rung 5 — OIDC Device Token

CLI agents obtain an OIDC token through the device flow using `ai-memory auth login oidc-device`. The token is stored and sent as a bearer on all HTTP calls when no static root token is present, falling back to the static root token if one is configured. This flow is documented in [`docs/security.md`](https://github.com/akitaonrails/ai-memory/blob/main/docs/security.md) at lines 55–73.

## Key Security Properties

Beyond the rung structure, ai-memory embeds several defensive controls directly into the authentication stack to harden both machine and human paths.

**Constant-time token comparison** prevents timing side-channels when checking bearer tokens. The root token comparison explicitly uses `subtle::ConstantTimeEq` in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) at lines 15–17 to ensure that comparison duration does not vary with the number of matching prefix bytes.

**Secure session cookies** can be enforced by setting `AI_MEMORY_AUTH__SECURE_COOKIE=true`, which marks HttpOnly session cookies as `Secure` for TLS-terminated deployments. This configuration is handled in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) at lines 73–78 and prevents accidental transmission of session material over unencrypted links.

**Rate-limited login attempts** protect the human authentication path from brute-force attacks. The `LoginLimiter` caps failed attempts per IP address to 10 attempts per 60 seconds and evicts old entries to prevent unbounded memory growth, as shown in [`crates/ai-memory-mcp/src/human_auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/human_auth.rs) at lines 22–36.

**Proxy-assertion safety** hardens the trusted proxy rung by rejecting requests where `X-Memory-Actor-*` headers appear more than once or are malformed. This validation in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) at lines 62–73 prevents header-spoofing attacks and ensures that only the legitimate proxy can assert actor identities.

## Practical Configuration and Code Examples

You can generate and configure credentials programmatically or through environment variables. Below are the exact patterns used in the ai-memory codebase.

Generate a cryptographically secure root bearer token:

```rust
use ai_memory_mcp::auth::generate_token_hex;
let token = generate_token_hex(32).expect("RNG failure"); // 64-hex chars
println!("Root token: {}", token);

```

Configure the server with a static token, secure cookies, and a trusted proxy:

```toml

# .env (or environment)

AI_MEMORY_AUTH_TOKEN="9f1a…"
AI_MEMORY_AUTH__SECURE_COOKIE=true
AI_MEMORY_AUTH_PROXY_BEARER="proxy-secret"

```

Check the authentication level inside an Axum handler:

```rust
use axum::Extension;
use ai_memory_core::AuthLevel;

async fn protected(Extension(level): Extension<AuthLevel>) -> impl IntoResponse {
    match level {
        AuthLevel::Root => "root-only operation".into(),
        AuthLevel::User => "user-level operation".into(),
        AuthLevel::Anonymous => (StatusCode::UNAUTHORIZED, "login required"),
    }
}

```

Log in through the web UI and store the HttpOnly session cookie:

```bash
curl -X POST http://localhost:49374/auth/login \
     -d '{"username":"alice","password":"s3cr3t"}' \
     -c cookies.txt

```

Authenticate the CLI via OIDC device flow:

```bash
ai-memory auth login oidc-device \
    --issuer "https://idp.example.com/realms/team" \
    --client-id "ai-memory-cli"

# Subsequent `ai-memory status` automatically uses the stored token.

```

## Summary

- ai-memory uses a **six-rung security model** that starts at unauthenticated loopback access and scales to OIDC-backed CLI tokens.
- **Machine endpoints** (`/mcp`, `/hook`, `/workstream`) rely on bearer tokens, database API keys, or trusted proxy assertions, never browser sessions.
- **Human access** is protected by Argon2 passwords, CSRF guards, rate-limited logins, and HttpOnly session cookies restricted to `/web` routes.
- All token comparisons use **constant-time checks**, and proxy-asserted headers are strictly validated to prevent spoofing.

## Frequently Asked Questions

### What is the default ai-memory security model for local development?

The default deployment uses **rung 0**, which disables authentication entirely and accepts any request on `127.0.0.1`. The `require_bearer` middleware injects an anonymous `ActorContext` when `AuthState::enabled()` is false, making local experimentation possible without configuring tokens.

### How does ai-memory prevent timing attacks on bearer tokens?

The server compares the root bearer token using `subtle::ConstantTimeEq` instead of a standard string equality check. This constant-time comparison logic in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) ensures that the comparison timing does not leak information about the secret token.

### Can browser session cookies authenticate MCP or webhook endpoints?

No. The human session cookie mechanism is explicitly restricted to browser routes under `/web`. As implemented in [`crates/ai-memory-mcp/src/human_auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/human_auth.rs), sessions never authenticate `/mcp`, `/hook`, or `/workstream` endpoints, keeping machine and human authentication paths completely separated.

### How does the trusted proxy rung prevent header spoofing?

The server rejects any request where `X-Memory-Actor-*` headers appear more than once or contain malformed values. This validation in [`crates/ai-memory-mcp/src/auth.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/auth.rs) ensures that only the trusted proxy bearer can successfully assert identities, and arbitrary clients cannot inject actor headers to escalate privileges.