add Dockerfile + deployment docs; make config dbPath optional

node:24-bookworm-slim image with git for the read-only vault endpoints,
non-root user with system-wide git safe.directory (mounted vaults are
usually owned by a host uid), /config + /data + /vaults mount
conventions baked in via BRAINDUMP_CONFIG/BRAINDUMP_DB, and an
/api/health healthcheck. README gains a Deployment section (mounts,
claude-sdk skill/auth requirements in-container, compose example,
reverse-proxy SSE buffering notes).

dbPath in the config file now defaults to ./braindump.sqlite instead of
being required — it was validated before the BRAINDUMP_DB env override
was applied, so the image's baked-in db path couldn't rescue a config
that omitted it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 02:44:06 +00:00
co-authored by Claude
parent 9d71692402
commit ec6c4e8df0
4 changed files with 90 additions and 1 deletions
+8
View File
@@ -0,0 +1,8 @@
node_modules
dist
.git
*.sqlite
*.sqlite-*
config.jsonc
Dockerfile
.dockerignore
+34
View File
@@ -0,0 +1,34 @@
FROM node:24-bookworm-slim
# git is needed for the read-only /git/status and /git/diff endpoints
RUN apt-get update \
&& apt-get install -y --no-install-recommends git ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY tsconfig.json config.example.jsonc ./
COPY src ./src
COPY scripts ./scripts
# Non-root user. Mounted vaults are usually owned by a host uid that doesn't
# match the container user, which git refuses to touch by default — allow it
# system-wide (read-only endpoints only; braindump never commits).
RUN useradd -m braindump \
&& git config --system --add safe.directory '*' \
&& mkdir -p /data /config /vaults \
&& chown braindump /data
USER braindump
ENV BRAINDUMP_CONFIG=/config/config.jsonc \
BRAINDUMP_DB=/data/braindump.sqlite \
NODE_ENV=production
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD node -e "fetch('http://localhost:'+(process.env.PORT||3000)+'/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["npx", "tsx", "src/bin/braindump-web.ts"]
+45
View File
@@ -26,6 +26,51 @@ Config is JSONC; path from `BRAINDUMP_CONFIG` (default `./config.jsonc`). `"${EN
There is **no auth** — put it behind an authenticating reverse proxy. CORS origins are configurable (`app://obsidian.md` is in the example for Obsidian plugins).
## Deployment (Docker)
```sh
docker build -t braindump .
```
The image expects three mounts (paths inside the container are up to you; these match the defaults baked into the image via `BRAINDUMP_CONFIG=/config/config.jsonc` and `BRAINDUMP_DB=/data/braindump.sqlite`):
| Mount | Purpose |
|---|---|
| `/config/config.jsonc` | your config — point `vaults[].path` at the container-side vault paths |
| `/data` | persistent volume for the SQLite db |
| `/vaults/...` | your vault(s), read-write |
Backend-specific requirements:
- **`claude-sdk`**: pass `ANTHROPIC_API_KEY`, and mount the brain-dump skill into the container user's home so the SDK's skill loading finds it: `-v ~/.claude/skills/brain-dump:/home/braindump/.claude/skills/brain-dump:ro`.
- **`openai-compat`**: mount the skill file wherever `skillPath` in your config points (e.g. `/config/SKILL.md`), and pass any `${ENV_VAR}` referenced by the config (e.g. `OPENROUTER_API_KEY`).
Example `compose.yaml`:
```yaml
services:
braindump:
build: . # or image: gitea.codinget.me/codinget/braindump
ports: ["3000:3000"]
environment:
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
volumes:
- ./config.jsonc:/config/config.jsonc:ro
- braindump-data:/data
- ~/git/brain:/vaults/brain
- ~/.claude/skills/brain-dump:/home/braindump/.claude/skills/brain-dump:ro
volumes:
braindump-data:
```
with the corresponding config using container paths: `"vaults": [{ "name": "brain", "path": "/vaults/brain" }]` and `"skillPath": "/home/braindump/.claude/skills/brain-dump/SKILL.md"`.
Notes:
- The container runs as a non-root `braindump` user; git `safe.directory` is pre-configured system-wide so the read-only git endpoints work on vaults owned by your host uid. Vault *writes* need the mounted vault to be writable by the container user (`chmod`/`chown` or a matching uid via `user:`).
- The healthcheck and config default both use port 3000; if you change the port, change both (`PORT` env works too, and overrides the config).
- **Reverse proxy + SSE**: disable response buffering for `/api/sessions/*/messages` (nginx: `proxy_buffering off;`, or honor the `X-Accel-Buffering: no` pattern) and raise/disable the proxy read timeout for long turns — heartbeat comments are sent every ~15s to keep the connection alive. This is also where your auth goes.
## API
Base path `/api`. Errors are `{"ok":false,"error":"<code>"}`.
+3 -1
View File
@@ -145,7 +145,9 @@ export function parseConfig(raw: unknown): Config {
const port = obj.port === undefined ? 3000 : obj.port;
if (typeof port !== "number") errors.push("port must be a number");
const dbPath = obj.dbPath;
// Optional: BRAINDUMP_DB overrides it after parsing, and container images
// bake that env in — so a missing dbPath just falls back to the default.
const dbPath = obj.dbPath === undefined ? "./braindump.sqlite" : obj.dbPath;
if (typeof dbPath !== "string" || !dbPath) errors.push("dbPath must be a non-empty string");
const cors = obj.cors;