MemoDocumentation
EN

Security Model

Memo is designed with a local-first, privacy-respecting security architecture.

API Key Encryption

All provider API keys (OpenAI, Anthropic, Google, etc.) are encrypted at rest using AES-256-GCM. Keys are derived from a machine-specific secret stored in the OS keyring where available, falling back to a file-based secret with restricted permissions. Keys are never logged, exported, or transmitted in plaintext.

Rate Limiting

A token-bucket algorithm enforces per-IP rate limits on the API server:

Endpoint Rate
/api/chat 30 requests / minute
/api/embeddings 60 requests / minute
All other endpoints 120 requests / minute

Burst capacity is 2x the sustained rate. Rate limit headers (X-RateLimit-*) are included in API responses.

File Upload Security

  • MIME Detection: File types are validated by magic bytes, not file extensions. Only whitelisted types are accepted.
  • Import Path Validation: Import file paths are sanitized to prevent path traversal attacks.
  • Size Limit: Request bodies are capped at 50 MB. Larger payloads receive a 413 Request Entity Too Large response.

File System Security

  • Configuration files (config.yaml, memo.db) are created with 0600 permissions (owner read/write only).
  • The data directory (~/.memo/) is created with 0700 permissions.
  • Temporary files use os.CreateTemp and are cleaned up on process exit.

Cloud Sync Encryption

Sync data is end-to-end encrypted:

  1. A 256-bit key is derived from the user's passphrase using Argon2id.
  2. Each sync entry is individually encrypted with AES-256-GCM and a random nonce.
  3. The server receives only ciphertext — it cannot read user data.
  4. The passphrase is never transmitted to the server; all encryption/decryption happens client-side.

Agent Sandbox

When an agent executes tools:

  • File system access is confined to the designated workspace directory.
  • Shell commands run with a configurable timeout (default: 30 seconds).
  • Network access from tools is restricted to an allowlist of domains.
  • Each agent session has a unique sandbox directory, cleaned up on session end.

WhatsApp Integration

The WhatsApp bridge uses a mutex to prevent concurrent message processing from the same conversation, avoiding race conditions on session state. Messages are queued and processed sequentially per chat.

Remote Access & Self-Hosting Authentication (v3.5.5)

As of v3.3.3, every request against a Remote Access connection (LAN or ngrok tunnel) had to present an access token — before that, anyone who could reach that address at all could read provider API keys, wipe data, or run agent commands with zero credentials. As of v3.5.5, that single-shared-token scheme has been replaced with a full authentication system, built for self-hosting on a Pi/home server/VPS/Docker but equally in effect for LAN/ngrok/Tailscale Remote Access on desktop.

Four Auth Modes

Selectable via Settings or memo remote set-mode:

Mode Credential required
none None — only appropriate on a fully trusted private network
token (default once bound off-loopback) A per-device token, X-Memo-Token header or Authorization: Bearer
password Username + argon2id-hashed password, issues a signed session
token_password Either a valid token or a valid password

Loopback Exemption

A request whose actual source IP is loopback (127.0.0.0/8, ::1) is trusted in every mode, with no credential — this is keyed on the request's real origin, not on what URL the client believes it's talking to, so it only ever covers software running on the same machine (the installed desktop app talking to 127.0.0.1). A request arriving from a LAN IP is still gated normally, even from the same physical box.

Passwords, Sessions, and Devices

  • Passwords are hashed with argon2id; login attempts are rate-limited with a brute-force lockout.
  • Sessions are short-lived signed tokens — 12 hours by default, 30 days with "remember me".
  • Per-device tokens are hashed at rest, shown once at creation, and individually revocable (memo remote revoke-device <id>) without affecting any other device. memo remote rotate-token <id> revokes and re-issues a token under the same device name in one step, for a leaked token that needs to stop working immediately.
  • --password/--key are never required as plain CLI arguments (visible via ps, left in shell history) — omit them and every CLI command that needs one prompts for it interactively, hidden.

Multi-Account

Self-hosted Memo supports multiple accounts with admin/user roles, managed from Settings → Accounts. Every screen in the app — chat, Settings, Developer Options — goes through the same first-run setup gate and login gate rather than each screen independently tracking auth state.

CORS Fix

A CORS origin-validation bypass in the remote-access gate (CWE-346) was found in an adversarial pass over the whole auth surface and closed in v3.5.5.

The Developer API Gateway shares this same auth system when its own "Require API Key" toggle is enabled. See Self-Hosting → Known Limitations for what's explicitly not covered yet (built-in TLS, an independent adversarial penetration test).

Agent Sandbox Hardening (v3.3.3)

  • The dangerous-command filter's pattern matching was hardened to close a gap that let a handful of specific destructive commands (e.g. wiping a root or home directory) slip past the safety check. Every agent action still requires explicit user approval regardless.
  • A symlink sandbox-escape was fixed: a symlink inside a project pointing at a file that didn't yet exist could previously let a file-editing tool write outside the sandboxed project directory.

Dependency Security

Two govulncheck-flagged vulnerabilities in third-party dependencies were patched in v3.3.3: an infinite-loop bug in golang.org/x/text (reachable through the WhatsApp integration's profile-picture lookup) and a related fix in golang.org/x/net.