Security model
What protects the daemon, and what the boundaries are.
The API token
The daemon binds to 127.0.0.1 and requires Authorization: Bearer <token> on
every route except /status. Without this, any web page you visit could reach
http://localhost:7331 and read or delete your collections. The token is
generated on first serve, stored at ~/.config/bookmark-context/token (mode
0600), and pasted once into the extension’s Settings.
/status stays unauthenticated so the extension can tell “daemon down” apart
from “token rejected”. It returns only {status, version}.
SSRF protection
The daemon fetches bookmark URLs server-side, so an unvalidated host is a
request-forgery vector aimed at cloud metadata endpoints and LAN services.
BookmarkCreate rejects any URL that isn’t http/https, has no host, or
resolves to a private, link-local, reserved, multicast, or .local/.internal
address. Loopback is deliberately allowed — bookmarking your own dev server is a
real use case.
The MCP server bypasses all of this
It reads SQLite and Chroma directly, with no token and no HTTP. Anything that can
run bookmark-context mcp can read every collection. That’s the same trust
boundary as the files themselves.
Trace redaction
Trace logs record a query as a length, never its text, and a failed tool call as
its exception type without the message. The raw-protocol proxy (mcp_debug.py)
does log full message bodies including queries — use it only while debugging.
CORS
Access-Control-Allow-Origin: *. This is intentional: the token is the control,
not the origin check. A browser without the token gets a 401 regardless of
origin.
Last updated Sep 9, 2026