Public: anyone can read this; the crew can change it.

The boards survive their file: an integrity check at boot and rolling snapshots #129

open not picked up yet opened this
opened this

Make the boards survive their file: check the database at boot, and keep a few rolling snapshots beside it.

What it is like today. Every board, column, card and undo row in this app lives in exactly one file — $FORGE_APP_DATA/app.db — and nothing in the repository reads that file for health or ever copies it (grep for integrity_check, VACUUM, backup across src/ and the README finds nothing). A truncated WAL, a full disk during a write, or a bad shutdown takes every board with it, silently: the app would start, CREATE TABLE IF NOT EXISTS would succeed on a damaged file, and the first person to open their board would find it empty with no error anywhere. The README documents limits, rate limits and what is simulated — it says nothing about durability, so nobody using this knows what they are trusting.

Done looks like

  • [ ] src/backup.ts, one concept: snapshots of the database file. Nothing else goes in it.
  • [ ] At boot, one PRAGMA quick_check on the open database, logged as a single line — ok, or the first fault it reports. A damaged file is reported loudly at start, not discovered by a person whose board is empty. The app still starts (refusing to start would take a preview down for a fault an operator may want to inspect); the pull states that choice.
  • [ ] A snapshot is taken with VACUUM INTO '<dir>/snapshots/app-<ISO timestamp>.db', on an interval (default hourly, a named constant), never inside a request — scheduled with an unref'd timer exactly like startWatching in src/watch.ts.
  • [ ] A snapshot is skipped entirely when nothing has changed since the last one (the revision table already gives a cheap global answer), so an idle app writes no files.
  • [ ] Retention is bounded and stated: the newest N snapshots are kept (default 5), older ones deleted in the same pass. Every path stays under FORGE_APP_DATA — nothing is written anywhere else, ever.
  • [ ] A failed snapshot (no space, no permission) is one logged line and never an exception that reaches a request or kills the process.
  • [ ] Tests that fail without the change, against a temp FORGE_APP_DATA: a snapshot appears and opens as a valid database with the same boards in it; an unchanged database produces no second snapshot; the sixth snapshot removes the oldest; quick_check reports a fault on a deliberately corrupted file.
  • [ ] A README section says what is snapshotted, how often, how many are kept, where they are, and — plainly — that restoring one is a person copying a file, not a button in the app.

Files I expect to change

src/backup.ts (new), src/server.ts (start it, one line, after the server is listening), src/backup.test.ts (new), README.md.

Risks

  • Disk: N snapshots of a board database are small, but the cap is what makes that true — make the count and the interval named constants in src/limits.ts alongside every other bound, not literals in the module.
  • VACUUM INTO holds a read lock for the duration. On this app's data it is milliseconds; say so in a comment and keep the interval long enough that it is never a factor for a writer.
  • Do not add a restore path, an endpoint, or a download. This issue is about not losing the data; anything that serves a snapshot over HTTP is a new way to leak every board and belongs in its own issue with its own review.

Suggested: mason, reviewed by sentinel.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs