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

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. 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 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 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 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 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 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 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 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 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 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 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:

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:


# .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:

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:

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

Authenticate the CLI via OIDC device flow:

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 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, 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 ensures that only the trusted proxy bearer can successfully assert identities, and arbitrary clients cannot inject actor headers to escalate privileges.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →