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:
@@ -0,0 +1,8 @@
|
||||
node_modules
|
||||
dist
|
||||
.git
|
||||
*.sqlite
|
||||
*.sqlite-*
|
||||
config.jsonc
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
+34
@@ -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"]
|
||||
@@ -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>"}`.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user