From 97a99e0225cb52036ad0510753caaf5668b49fe8 Mon Sep 17 00:00:00 2001 From: Graham Barber Date: Mon, 13 Jul 2026 08:15:17 -0700 Subject: [PATCH] add docker packaging with compose deployment --- .dockerignore | 17 +++++++++++++++++ Dockerfile | 33 +++++++++++++++++++++++++++++++++ README.md | 21 ++++++++++++++++++--- docker-compose.yml | 15 +++++++++++++++ 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..20468f8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,17 @@ +node_modules +build +.svelte-kit +data +.env +.env.* +!.env.example +.git +.jj +.claude +.vscode +openspec +Dockerfile +docker-compose.yml +README.md +PRODUCT.md +DESIGN.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2200bf4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +# Build stage: install dependencies and produce the adapter-node output. +FROM denoland/deno:2.9.2 AS build +WORKDIR /app +ENV DENO_NO_UPDATE_CHECK=1 + +COPY package.json deno.json deno.lock ./ +RUN deno install + +COPY . . +RUN deno task build + +# Runtime stage. The adapter-node output resolves npm packages at runtime, +# so node_modules and the manifests come along; dev tooling does not run. +FROM denoland/deno:2.9.2 +WORKDIR /app +ENV DENO_NO_UPDATE_CHECK=1 + +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/package.json /app/deno.json /app/deno.lock ./ +COPY --from=build /app/build ./build +COPY --from=build /app/migrations ./migrations + +# The SQLite database lives on a volume; treat it as secret-grade and back it up. +RUN mkdir -p /data && chown -R deno:deno /data /app +USER deno +VOLUME /data +ENV DB_PATH=/data/quantum.db +ENV PORT=3000 +EXPOSE 3000 + +# -A is acceptable here: the container boundary is the sandbox. +# --unstable-cron powers the daily SimpleFIN sync. +CMD ["deno", "run", "--unstable-cron", "-A", "build/index.js"] diff --git a/README.md b/README.md index 9ec3dc9..0fb2147 100644 --- a/README.md +++ b/README.md @@ -45,14 +45,28 @@ expense per category and net worth over time. 4. **Build and run** + With Docker (recommended for deployment): + + ```sh + docker compose up -d --build + ``` + + The compose file reads `.env` for `APP_URL`, `ALLOWED_DIDS`, and + `OAUTH_PRIVATE_KEY_JWK`, publishes port 3000, and stores the database on + the `quantum-data` named volume (`DB_PATH` defaults to `/data/quantum.db` + inside the container). Back up that volume — it is the only state. + + Or directly on the host: + ```sh deno install # once, after every dependency change deno task build PORT=3000 deno task start ``` - Migrations apply automatically at startup. Run from the repo root (the - `migrations/` directory and `.env` are resolved relative to it). + Either way, migrations apply automatically at startup. When running on the + host, run from the repo root (the `migrations/` directory and `.env` are + resolved relative to it). 5. **First run** — log in with an allowlisted handle, open **Settings**, paste a SimpleFIN setup token (single-use; generate it at the Bridge), and the @@ -78,7 +92,8 @@ deno task check # svelte-check history. Restrict file permissions and treat backups with the same care. - **Back up `DB_PATH`** (plus its `-wal`/`-shm` siblings, or use `sqlite3 .backup`). Balance history powers the net-worth chart and cannot be - re-fetched: losing the file loses it. + re-fetched: losing the file loses it. Under Docker that means the + `quantum-data` volume. - Transaction backfill is limited to what banks return at first sync (~90 days). Balance snapshots start on day one and only grow. - All money is stored as integer cents; 2-decimal currencies are assumed. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..992e0cf --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + quantum: + build: . + ports: + - '3000:3000' + # Set APP_URL, ALLOWED_DIDS, and OAUTH_PRIVATE_KEY_JWK in .env next to + # this file (see .env.example). DB_PATH defaults to /data/quantum.db. + env_file: + - .env + volumes: + - quantum-data:/data + restart: unless-stopped + +volumes: + quantum-data: -- 2.51.2