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 — see docs/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:

  1. 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.
  2. The governance system (π, Phase 6) and rule engine (κ, Phase 1) require atomic multi-step transactions. The sync/async model affects how these are written.
  3. 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:sqlite or better-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 await everywhere, including in tight rule-engine loops
  • Less documentation and community examples than better-sqlite3
  • No pragma convenience wrappers; WAL mode must be set manually

Option B: Migrate to better-sqlite3

Pros:

  • Synchronous API — no await in 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-gyp and 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:sqlite API 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-sqlite3 for Phase 0) per R75 PR #114 depth pass. Revisit gate scheduled for R90 against Node 24 LTS + node:sqlite stability.

Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.