thedotmack/claude-mem · error
Worker bound to a non-loopback host with no…
Error message
Worker bound to a non-loopback host with no CLAUDE_MEM_TV_TOKEN — the full worker API, including provider API keys via GET /api/settings, is reachable from the network
What it means
When the worker starts bound to a non-loopback host while CLAUDE_MEM_TV_TOKEN is unset, everything except the TV paths is exposed unauthenticated on the network — including GET /api/settings, which returns provider API keys. The worker deliberately warns instead of refusing so documented docker setups (CLAUDE_MEM_WORKER_HOST=0.0.0.0) keep working.
Solutions
- Set CLAUDE_MEM_TV_TOKEN to a strong random value in the worker environment.
- Bind the worker to loopback (CLAUDE_MEM_WORKER_HOST=127.0.0.1) and reverse-proxy externally with auth.
- If Docker requires 0.0.0.0, keep the port unpublished from the host network or restrict with firewall rules.
- Never expose GET /api/settings publicly; if keys may have leaked, rotate provider API keys immediately.
Example fix
// before CLAUDE_MEM_WORKER_HOST=0.0.0.0 // after CLAUDE_MEM_WORKER_HOST=0.0.0.0 CLAUDE_MEM_TV_TOKEN=$(openssl rand -hex 32)
Defensive patterns
Strategy: validation
Validate before calling
const host = process.env.CLAUDE_MEM_WORKER_HOST ?? '127.0.0.1';
if (host !== '127.0.0.1' && host !== 'localhost' && !process.env.CLAUDE_MEM_TV_TOKEN) {
throw new Error('Refusing non-loopback bind without CLAUDE_MEM_TV_TOKEN');
} Prevention
- Always set CLAUDE_MEM_TV_TOKEN when binding beyond loopback
- Default CLAUDE_MEM_WORKER_HOST to 127.0.0.1 in local dev
- Review what GET /api/settings exposes before exposing the worker to a network
- Rotate provider API keys if the worker was publicly reachable without a token
When it happens
Trigger: start (called by main) with host set to 0.0.0.0, a LAN IP, a Docker/bridge address, etc., while no CLAUDE_MEM_TV_TOKEN is present in the environment.
Common situations: Docker installs following docs/docker.md with CLAUDE_MEM_WORKER_HOST=0.0.0.0; running the worker on a shared server; exposing the port through a router/port-forward without a token.
Related errors
- Access denied: " " resolves outside the workspace ( ). MCP…
- Admin endpoints are only accessible from localhost
- auth_invalid
- auth_invalid
- BadRequest
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/cf8941270e6a4040.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/worker-service.ts:486
// Must run before startSupervisor(): its validateWorkerPidFile() removes
// the dead previous run's stale PID file, which crash detection needs.
this.detectPreviousShutdown();
await startSupervisor();
await this.server.listen(port, host);
if (this.tvToken) {
// Operators need to see, in the log, that a remote surface is open.
// Never log the token itself.
logger.info('SYSTEM', 'Observation TV remote broadcast enabled', {
host,
allowedPaths: ['/tv', '/tv.html', '/stream', 'GET /api/observations'],
});
} else if (host !== '127.0.0.1' && host !== '::1' && host !== '::ffff:127.0.0.1' && host !== 'localhost') {
// Warn, do not refuse to bind: docs/docker.md tells people to set
// CLAUDE_MEM_WORKER_HOST=0.0.0.0, and refusing would break that install.
logger.warn(
'SECURITY',
'Worker bound to a non-loopback host with no CLAUDE_MEM_TV_TOKEN — the full worker API, including provider API keys via GET /api/settings, is reachable from the network',
{ host }
);
}
writePidFile({
pid: process.pid,
port,
startedAt: new Date().toISOString()
});
getSupervisor().registerProcess('worker', {
pid: process.pid,
type: 'worker',
startedAt: new Date().toISOString()
});
View on GitHub (pinned to d8bc9755e7)