feat: adopt bchen-sqlite-migrate package; replace inline SCHEMA_DDL #9

Merged
brendan merged 1 commits from feat/adopt-sqlite-migrate into main 2026-05-12 15:08:33 +00:00
Owner

Summary

Phase 3 of the cross-project sqlite-migrate adoption — port nanodrop to consume bchen-sqlite-migrate@v0.1.0 (released at https://gitea.bchen.dev/brendan/sqlite-migrate). Replaces the inline db.exec(...) block in src/db/schema.ts with applyMigrations(db, MIGRATIONS_DIR, { genesisProbeTable: 'users' }).

Follows buchinese PR #14 (merge_commit 19811d73…) and authd PR #15 (merge_commit 0095f105…) structurally.

Changes

  • package.json: + bchen-sqlite-migrate git+https dep pinned to #v0.1.0 (resolves to commit 7dbce663…); + db:migrate / db:status / db:stamp npm scripts.
  • package-lock.json: regenerated from scratch with rm -rf node_modules package-lock.json && npm install --include=optional.
  • src/db/migrations/0001_init.sql: NEW, captures current initDb DDL — 4 statements: users, files, login_attempts tables + idx_login_attempts_locked_until index.
  • src/db/schema.ts: rewritten as thin wrapper around applyMigrations; reads DB_MIGRATIONS_STAMP_GENESIS env var (optional, default-off — matches authd/buchinese behavior).
  • src/scripts/{_db-cli,db-migrate,db-status,db-stamp}.ts: NEW operator CLIs, mirror authd/buchinese.
  • tests/unit/migrations-byte-stable.test.ts: NEW, pins sha256(0001_init.sql) = 34f092b4bb8544a48acfee0fad08d51b1b75fedf4ffdfbcb790d2656d0f1d57a as the nanodrop-specific immutability guard.

Security

None. Pure dep swap inside the server-side DB-init boundary. No new HTTP routes, auth gates, secrets, env-var leaks, or rate-limit-relevant surface. MIGRATIONS_DIR is a compile-time constant (no traversal surface); applyMigrations invokes db.exec on trusted static repo content; the package's FILENAME_RE regex defeats path traversal at filename layer. db-stamp reads process.argv[2] but is an operator-only CLI (not web-reachable) and stampMigration validates the version against a real file. Peer-dep better-sqlite3 already pinned at ^12.6.2. Lockfile commit-SHA-pinned via #v0.1.0 tag (resolves to commit 7dbce663…).

Environment

DB_MIGRATIONS_STAMP_GENESIS — optional, default-off. Read as === '1'. Already declared as optional in authd/buchinese precedent; no deploy-manifest wiring needed (production nanodrop DBs already have all tables present, so genesis-stamp fires automatically via the users-probe regardless of the env var). Setting it to 1 is only needed if a corrupted DB needs an explicit stamp without re-executing the genesis migration. No env-blocked gate triggered.

Lockfile-verified

npm ci on clean node_modules (sandbox-side, exit 0). docker build . deferred to first prod deploy — docker daemon socket not reachable from sandbox (same caveat as authd PR #15 and buchinese PR #14).

Tests

  • npm run build (tsc --noEmit) clean.
  • npm test green: 131 passed across 19 files (was 130 + 1 new byte-stable test).
  • Existing tests (130) call initDb(':memory:'), exercising the cold-DB path through applyMigrations on every run.

Expected first-deploy log

Production boot after this PR merges should show:

WARN genesis-stamp: marked 0001_init as applied without executing (detected pre-existing 'users' table)
migrations: 1 applied, 0 pending

Subsequent boots: migrations: 0 applied, 0 pending. If you see CREATE TABLE … already exists instead, the genesis probe is misconfigured — file a bug.

## Summary Phase 3 of the cross-project sqlite-migrate adoption — port nanodrop to consume `bchen-sqlite-migrate@v0.1.0` (released at https://gitea.bchen.dev/brendan/sqlite-migrate). Replaces the inline `db.exec(...)` block in `src/db/schema.ts` with `applyMigrations(db, MIGRATIONS_DIR, { genesisProbeTable: 'users' })`. Follows buchinese PR #14 (merge_commit `19811d73…`) and authd PR #15 (merge_commit `0095f105…`) structurally. ## Changes - `package.json`: + `bchen-sqlite-migrate` git+https dep pinned to `#v0.1.0` (resolves to commit `7dbce663…`); + `db:migrate` / `db:status` / `db:stamp` npm scripts. - `package-lock.json`: regenerated from scratch with `rm -rf node_modules package-lock.json && npm install --include=optional`. - `src/db/migrations/0001_init.sql`: NEW, captures current `initDb` DDL — 4 statements: `users`, `files`, `login_attempts` tables + `idx_login_attempts_locked_until` index. - `src/db/schema.ts`: rewritten as thin wrapper around `applyMigrations`; reads `DB_MIGRATIONS_STAMP_GENESIS` env var (optional, default-off — matches authd/buchinese behavior). - `src/scripts/{_db-cli,db-migrate,db-status,db-stamp}.ts`: NEW operator CLIs, mirror authd/buchinese. - `tests/unit/migrations-byte-stable.test.ts`: NEW, pins `sha256(0001_init.sql) = 34f092b4bb8544a48acfee0fad08d51b1b75fedf4ffdfbcb790d2656d0f1d57a` as the nanodrop-specific immutability guard. ## Security None. Pure dep swap inside the server-side DB-init boundary. No new HTTP routes, auth gates, secrets, env-var leaks, or rate-limit-relevant surface. `MIGRATIONS_DIR` is a compile-time constant (no traversal surface); `applyMigrations` invokes `db.exec` on trusted static repo content; the package's `FILENAME_RE` regex defeats path traversal at filename layer. `db-stamp` reads `process.argv[2]` but is an operator-only CLI (not web-reachable) and `stampMigration` validates the version against a real file. Peer-dep `better-sqlite3` already pinned at `^12.6.2`. Lockfile commit-SHA-pinned via `#v0.1.0` tag (resolves to commit `7dbce663…`). ## Environment `DB_MIGRATIONS_STAMP_GENESIS` — optional, default-off. Read as `=== '1'`. Already declared as optional in authd/buchinese precedent; no deploy-manifest wiring needed (production nanodrop DBs already have all tables present, so genesis-stamp fires automatically via the `users`-probe regardless of the env var). Setting it to `1` is only needed if a corrupted DB needs an explicit stamp without re-executing the genesis migration. No env-blocked gate triggered. ## Lockfile-verified `npm ci` on clean `node_modules` (sandbox-side, exit 0). `docker build .` deferred to first prod deploy — docker daemon socket not reachable from sandbox (same caveat as authd PR #15 and buchinese PR #14). ## Tests - `npm run build` (tsc --noEmit) clean. - `npm test` green: 131 passed across 19 files (was 130 + 1 new byte-stable test). - Existing tests (130) call `initDb(':memory:')`, exercising the cold-DB path through `applyMigrations` on every run. ## Expected first-deploy log Production boot after this PR merges should show: ``` WARN genesis-stamp: marked 0001_init as applied without executing (detected pre-existing 'users' table) migrations: 1 applied, 0 pending ``` Subsequent boots: `migrations: 0 applied, 0 pending`. If you see `CREATE TABLE … already exists` instead, the genesis probe is misconfigured — file a bug.
brendan added 1 commit 2026-05-12 15:08:11 +00:00
Phase 3 of the cross-project sqlite-migrate adoption — port nanodrop to
consume bchen-sqlite-migrate@v0.1.0. Replaces the inline db.exec(...)
block in src/db/schema.ts with applyMigrations(db, MIGRATIONS_DIR,
{ genesisProbeTable: 'users' }).

The genesis-probe (table 'users' exists) handles pre-existing prod DBs
automatically — first deploy after merge stamps 0001_init as applied
without re-executing, subsequent boots are no-ops.

Adds three npm scripts (db:migrate, db:status, db:stamp) and a
byte-stability test pinning sha256(0001_init.sql) so the migration is
treated as immutable history.
brendan merged commit 83a128f917 into main 2026-05-12 15:08:33 +00:00
Sign in to join this conversation.