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