Please do not open a public issue for security problems. Report privately via GitHub: go to the repo's Security tab → Report a vulnerability (new advisory). We aim to acknowledge within a few business days.
Lore is a public repo that serves a private brain. It is not a read-only app: it stores its own pages and memories in Postgres, and agents write to it. The protection is not "nothing writes" — it is that reading and writing are separate doors with separate credentials, and the console only ever holds the reading one.
- The console (
/api/call, used by the browser) is gated by the viewer auth below and passes"read"into the tool dispatcher.handleRpcdecides from the tool's own declaredaccess, so a write tool is unreachable from a viewer session no matter what the browser asks for. There is deliberately no second hand-written allowlist to drift out of step with the registry. - Agents (
POST /api/mcp,/import,/api/maintenance,/api/export) present a bearer:BRAIN_WRITE_TOKEN(read + write) orBRAIN_READ_TOKEN(read only). Both are ≥16 characters or refused outright, and compared in constant time./api/exportrequires the write token even though it only reads — a full dump bypasses the read surface's filter, so the read credential is not enough for it.
- A tool's
accessis the boundary. Never widen a tool fromwritetoreadto make a console feature convenient; add the feature to the agent surface instead. - Secrets are server-only.
BRAIN_*_TOKENis read only in server code (guarded byimport "server-only") and never reaches the browser. Never commit.env. - Auth fails closed, and a half-configured mode is an error rather than an
opening.
AUTH_MODE=passwordwith noUI_PASSWORDis refused — it does not fall through toALLOW_INSECURE.AUTH_MODE=noneis honored only with an explicitALLOW_INSECURE=1. AUTH_MODE=gatewaynever trusts an identity header on its own. TheX-Forwarded-Userheader is read only after the gateway has proved it is the gateway — a JWT verified againstAUTH_GATEWAY_JWKS_URL(signature, issuer, audience, expiry), orAUTH_GATEWAY_SHARED_SECRETcompared in constant time. With neither configured, gateway mode refuses every request. Do not add a "trust the header" option.- Credentials must not enter the brain. Every argument of every write tool is
screened for secrets before any handler runs, and every write tool refuses to
name a page in the reserved
memory/namespace. - Responses don't leak upstream errors; the API routes are rate-limited; a strict
Content-Security-Policy and standard security headers are set in
next.config.mjs.
Never deploy with ALLOW_INSECURE=1 reachable from the internet — it serves the
whole brain to anyone. Pick AUTH_MODE=gateway (behind Cloudflare Access,
oauth2-proxy, Authelia, or any proxy that can sign a JWT or set a secret header)
or AUTH_MODE=password, and make sure the origin is only reachable through that
layer. Treat BRAIN_WRITE_TOKEN as production-grade: it can write, delete, import
and export the entire brain.