ADR-001 — SQLite: node:sqlite vs better-sqlite3
Status: ACCEPTED Date: 2026-04-07 (proposed) · 2026-04-16 (accepted) Domain: Infrastructure (α System Core)
Context
⚠ R74.5 correction. The original context block below referenced “78+ tables” and “~484 tools (Phase 0 target)”. Both numbers are heritage donor artifacts, not Phase 0 targets. ADR-004 locks the Phase 0 tool surface at exactly 19 tools; the Phase 0 schema is an earned, low-teens-table layout in
data/colibri.db(the “78 tables” figure is the pre-R53 AMS donor schema and is explicitly not a target — seedocs/architecture/data-model.md§2 and CLAUDE.md §9.1). The decision — better-sqlite3 over node:sqlite — is still load-bearing; only the surrounding numbers were stale.
Colibri currently uses node:sqlite — the built-in SQLite module added to Node.js 22.5.0 (experimental as of April 2026). The alternative better-sqlite3 is a mature, battle-tested synchronous SQLite binding used extensively across the Node.js ecosystem.
The choice matters because:
- SQLite is the single source of truth for all Colibri data. Phase 0 earns a small number of tables (low teens) in
data/colibri.db; the donor AMS schema had 78 tables but that is not a Phase 0 target and must not be cited as one. - The governance system (π, Phase 6) and rule engine (κ, Phase 1) require atomic multi-step transactions. The sync/async model affects how these are written.
- The database layer will be called by the exactly 19 Phase 0 MCP tools (ADR-004) and, over the full roadmap, by a ceiling of 60–80 tools across ten business domains. The “~484 tools” figure cited in older drafts is heritage donor AMS and is not a Colibri target.
Current state:
- Colibri will use either
node:sqliteorbetter-sqlite3(decision pending) - Schema will be loaded via database schema files
- Connection management via repository pattern
Decision
Option B — adopt better-sqlite3 for Phase 0. The synchronous API, production maturity, and alignment with the rule-engine (κ) and governance (π) determinism requirements outweigh the native-addon cost. Phase 0 Jest tests will run against a pinned better-sqlite3 version; CI builds the native module once per platform and caches it.
Decision gate. Migration may be revisited in R90 when Node 24 LTS + node:sqlite stability is confirmed. At that point the PM should weigh: (1) whether node:sqlite has shipped a stable sync API, (2) whether the rule engine has grown dependencies on better-sqlite3-specific features (e.g. Database.prototype.serialize, user-defined aggregates), and (3) whether the CI toolchain cost of the native module has proven material.
Until R90 the decision stands: better-sqlite3 is the Phase 0 binding; rule engine, governance, and all domain repositories may use its synchronous transaction API without apology.
Options
Option A: Stay with node:sqlite (built-in)
Pros:
- Zero native addon dependency (no
node_modules/better-sqlite3/build/) - No compilation step needed on deploy
- Long-term: will become stable in a future Node.js LTS
Cons:
- Still experimental (as of Node 22.x); API may change before stabilization
- Async-only API requires
awaiteverywhere, including in tight rule-engine loops - Less documentation and community examples than better-sqlite3
- No
pragmaconvenience wrappers; WAL mode must be set manually
Option B: Migrate to better-sqlite3
Pros:
- Synchronous API — no
awaitin the rule engine, governance loops, or migration scripts - Stable, well-documented, 8+ years of production use
- Better performance benchmarks for read-heavy workloads (no event-loop overhead per query)
- Full WAL mode support, transaction savepoints, named parameters
Cons:
- Native addon (compiled C++) — requires
node-gypand platform toolchain on CI and deploy - Additional dependency to audit and maintain
- Requires a thin shim layer rewrite in
src/db/index.js(API surface is similar but not identical)
Consequences
If Option A (stay):
- No migration cost now
- Rule engine (κ) and governance (π) layers must be written async-first
- Risk:
node:sqliteAPI may change before Phase 1 work is done
If Option B (adopt better-sqlite3):
- Implementation estimate: 1-2 days to build database layer with better-sqlite3
- All domain code (κ, λ, π, θ, ι) can use synchronous transactions — simpler code
- CI pipeline must build native module on each platform
Alternatives Considered
- Prisma or Drizzle ORM: rejected — adds abstraction layer above SQLite; Colibri needs direct schema control for the rule engine’s deterministic execution requirements
- PostgreSQL: rejected — deployment simplicity (single SQLite file) is a design constraint
- D1 (Cloudflare): not applicable — Colibri is a local MCP server, not a serverless worker
References
- Target database layer specification in Phase 0 task P0.2
- Schema design in
docs/architecture/data-model.md(Phase 0 target is a low-teens-table earned layout; the “78 tables” figure there is heritage donor AMS, not a Colibri target) - better-sqlite3 docs
- node:sqlite docs
- Data Model — target schema design
Decision log
- 2026-04-07 — ADR proposed (PROPOSED status, pending PM decision).
- 2026-04-16 — Accepted Option B (
better-sqlite3for Phase 0) per R75 PR #114 depth pass. Revisit gate scheduled for R90 against Node 24 LTS +node:sqlitestability.