Skip to main content

Authentication

API (JWT)

  • Access token: 15-minute expiry, HS256
  • Refresh token: 7-day expiry
  • All protected routes require Authorization: Bearer {access_token}

MCP server

The MCP server supports dual transport:
  • stdio (Claude Code local): No auth needed, runs as a local process
  • Streamable HTTP (remote clients): OAuth 2.0 Authorization Code + PKCE
    • Dynamic Client Registration (RFC 7591)
    • Token lifetimes: access 1h, refresh 30d
    • Metadata discovery at /.well-known/oauth-authorization-server
A legacy bearer token (MCP_AUTH_TOKEN) is supported for backward compatibility.

Auth toggle

AUTH_ENABLED=false disables auth for local development. When disabled, only expose on localhost.

User isolation

Every query on user-owned data filters by user_id. Two layers enforce this:
  1. Application-level: Services explicitly filter by user_id in WHERE clauses
  2. Database-level: Row-level security (RLS) policies on 9 tables
Cross-user access returns 404 (not 403) to prevent ID enumeration.

Password handling

  • Hashed with argon2id
  • Legacy bcrypt hashes rehashed transparently on login
  • Never logged or returned in responses
  • Minimum 8 characters
User data is never sent to external LLMs without explicit opt-in:
  • LLM_CONSOLIDATION=false (default): Similar memories concatenated locally
  • LLM_CONSOLIDATION=true: Enables OpenAI API call for intelligent synthesis
  • The status MCP tool reports the current consent state
Document and memory text is sent to OpenAI for embedding generation (required for semantic search).

Environment secrets

Required in production: JWT_SECRET, DATABASE_URL, OPENAI_API_KEY. Never commit secrets to git. Use .env files excluded via .gitignore.