The Reflect daemon (reflect daemon) binds an HTTP server on port 4590 (default)
of the loopback interface only (127.0.0.1:4590). This document describes
the threat model, attack surface, and mitigation controls.
| Endpoint | Auth required | Notes |
|---|---|---|
GET /v1/ready |
No | Dependency-aware readiness probe |
POST /v1/chat |
Configurable | Creates or resumes a session; SSE streaming |
GET /v1/sessions |
Configurable | Lists persisted sessions |
GET /v1/sessions/:id |
Configurable | Retrieves a session |
DELETE /v1/sessions/:id |
Configurable | Deletes a session |
GET /v1/stats |
Configurable | Usage/cost statistics |
POST /v1/cancel |
Configurable | Cancels the active generation |
The daemon binds only to 127.0.0.1. Traffic never leaves the loopback
interface. An attacker on the same LAN cannot reach the daemon.
A malicious local process (malware, compromised script) can send HTTP requests
to 127.0.0.1:4590 without any network privilege.
Mitigation:
- Set
REFLECT_DAEMON_API_KEYand configure the daemon with--api-keyto requireAuthorization: Bearer <key>on all non-readiness endpoints. - The key is validated per-request; the daemon does not issue tokens.
Any local process can detect that port 4590 is open and identify reflect.
Mitigation: Low severity — this is unavoidable for a local HTTP service. The daemon does not expose version information on unauthenticated endpoints.
Session data (conversation history, tool outputs) is stored in SQLite at
~/.reflect/sessions/reflect.db. A local attacker with filesystem read access can
read this file directly regardless of the daemon's auth.
Mitigation:
- SQLite file permissions are
0600(owner-only read/write). - Use full-disk encryption (FileVault on macOS, LUKS on Linux) for complete protection.
A local attacker can flood the daemon with chat requests, consuming LLM API credits.
Mitigation:
- A global concurrency cap bounds in-flight generations (default 4, tuned
via
REFLECT_DAEMON_MAX_CONCURRENT). When the cap is hit, new/v1/chatrequests are refused with503instead of queuing unboundedly. - Per-IP token-bucket rate limiting:
/v1/chatis limited to ~30 req/min (burst 6) and other authenticated endpoints to ~10 req/min (burst 4). Excess requests get429. /v1/cancel(POST{ "session_id": ... }) aborts an in-progress generation so a runaway response can be stopped early.- Consider running the daemon behind an OS-level firewall rule if operating in a shared-machine environment.
- Remote network attackers: the daemon does not bind to
0.0.0.0; remote access is architecturally blocked. - Process privilege escalation: reflect does not run as root and does not use
setuid/setgid.
Set the daemon API key before starting the daemon:
# Option 1: environment variable (recommended for CI/automation)
export REFLECT_DAEMON_API_KEY="your-random-secret-here"
reflect daemon
# Option 2: CLI flag
reflect daemon --api-key "your-random-secret-here"All clients must then pass:
Authorization: Bearer your-random-secret-here
The SDK reads this from REFLECT_DAEMON_API_KEY automatically if the env var is
set.
reflect daemon --port 9000Or in ~/.reflect/settings.json:
{
"daemon": {
"port": 9000
}
}If reflect is running on a machine shared by multiple OS users (e.g., a dev server), set the API key and use OS firewall rules to restrict which local users can connect to port 4590:
# macOS — pf: allow only current user's processes (requires pf.conf editing)
# Linux — iptables: allow only the reflect-owner UID
sudo iptables -A OUTPUT -p tcp --dport 4590 -m owner ! --uid-owner $UID -j REJECTcmd/daemon.go— daemon entrypoint and auth middlewareinternal/daemon/— HTTP handlersSECURITY.md— vulnerability reportingdocs/SECURITY-DEVELOPER.md— credential storage model