fix: make Docker quickstart work out of the box + robust SQLite fallback#236
Conversation
The documented Docker/Podman quickstart failed on first run in two ways: - The server writes its SQLite data dir under ~/.claude/agent-dashboard by default, but the recommended ~/.claude bind mount is read-only, so the container crash-looped on `ENOENT: mkdir '/root/.claude/agent-dashboard'`. - Since the loopback-default hardening (GHSA-gr74-4xfh-6jw9) the server binds 127.0.0.1, but a container's loopback is a separate namespace the published port cannot reach, so `docker run -p 4820:4820` returned connection-reset. Fix in the image so both Compose and plain `docker run` work as-is: - Dockerfile: bake DASHBOARD_DATA_DIR=/app/data (writable volume) and DASHBOARD_HOST=0.0.0.0 (bind inside the container). - docker-compose.yml: publish the host port on 127.0.0.1 only, keeping the dashboard local-only by default — the trust boundary moves from the in-container bind to the host port publish, preserving the GHSA posture. Docs updated to match (README + VN/CN/KO, INSTALL, SETUP, DEPLOYMENT, wiki): plain-run examples use `-p 127.0.0.1:4820:4820`, prose explains the image defaults and how to opt into LAN exposure (publish on 0.0.0.0 + DASHBOARD_TOKEN). Wiki gains a "Local-only by default" callout with zh/vi translations and the required cache bump. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…don fails better-sqlite3's require() only loads its JS; the native addon is resolved lazily on the first `new Database(...)`. When the prebuilt binary is missing or ABI-mismatched (e.g. installed for a different Node version), require() succeeds but construction throws — and the real `new Database(DB_PATH)` runs outside the try/catch, so the server crashed at startup instead of using the intended node:sqlite fallback. Probe better-sqlite3 with a throwaway in-memory handle right after require, so a broken native binary falls back to node:sqlite now rather than crashing later. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
✅ All contributors have signed the CLA. Thank you! |
There was a problem hiding this comment.
Code Review
This pull request enhances container security and reliability by ensuring the agent dashboard is local-only by default. It updates the Dockerfile, docker-compose.yml, and documentation across multiple languages to bind the published container port to the host's loopback interface (127.0.0.1) instead of exposing it on all interfaces. Inside the container, the application binds to 0.0.0.0 (DASHBOARD_HOST) to allow port forwarding, and the SQLite data directory is redirected to a writable volume (/app/data). Additionally, server/db.js is updated to eagerly probe the better-sqlite3 native addon, ensuring a graceful fallback to node:sqlite if there is an ABI mismatch. I have no further feedback to provide as the changes are well-implemented and documented.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
I have read the CLA Document and I hereby sign the CLA |
Summary
Two independent, low-risk fixes (one commit each), both first-run failures for new users:
1.
fix(docker)— the documented Docker/Podman quickstart fails out of the boxFollowing the README/INSTALL/SETUP/DEPLOYMENT/wiki Docker instructions currently fails in one of two ways:
~/.claude/agent-dashboardby default, but the recommended~/.claudebind mount is read-only →Error: ENOENT: no such file or directory, mkdir '/root/.claude/agent-dashboard', container restarts forever.127.0.0.1, but a container's loopback is a separate namespace the published port cannot reach, sodocker run -p 4820:4820/ Compose returncurl: (56) Recv failure: Connection reset by peer.Fix (baked into the image so both Compose and plain
docker runwork as-is):Dockerfile:ENV DASHBOARD_DATA_DIR=/app/data(writable volume) +ENV DASHBOARD_HOST=0.0.0.0(bind inside the container).docker-compose.yml: publish the host port on127.0.0.1only → dashboard stays local-only by default. The trust boundary moves from the in-container bind to the host port publish, preserving the GHSA loopback posture (no LAN exposure without an explicit opt-in).Docs updated to match across all surfaces (README + VN/CN/KO, INSTALL, SETUP, DEPLOYMENT, wiki): plain-run examples use
-p 127.0.0.1:4820:4820, prose explains the image defaults and how to opt into LAN exposure (publish on0.0.0.0+ setDASHBOARD_TOKEN). Wiki gains a "Local-only by default" callout withzh/vitranslations and the requiredsw.jscache bump + asset?v=bump.2.
fix(server)— server crashes at startup when better-sqlite3's native addon is brokenrequire("better-sqlite3")only loads its JS; the native addon is resolved lazily on the firstnew Database(...). When the prebuilt binary is missing or ABI-mismatched (e.g.node_modulesinstalled under a different Node version),require()succeeds but construction throws — and the realnew Database(DB_PATH)inserver/db.jsruns outside thetry/catch, so the process dies at startup instead of using the intendednode:sqlitefallback.Fix: probe better-sqlite3 with a throwaway in-memory handle right after
require, so a broken native binary falls back tonode:sqlitenow rather than crashing later.Testing
npm run test:server→ 652/652 pass.docker compose up -d --build):/api/health→ 200, UI → 200, no restart loop, and the LAN IP is unreachable (local-only confirmed).server/db.jsnow loads vianode:sqlitewithout crashing.strong+ paragraph resolve tozhandvi);prettier --checkclean on all touched files.🤖 Generated with Claude Code