ADR-0013: Pure-Go SQLite driver (modernc.org/sqlite) — toolchain-free go install
- Status: Accepted
- Date: 2026-06-28
- Supersedes: 📝 ADR-0001 (the driver/cgo choice; the FTS5 requirement it established still holds) and, in part, 📝 ADR-0002 (the sqlite-vec loadable-extension path — the Go brute-force scan is the implemented backend)
Context
📝 ADR-0001 chose the cgo mattn/go-sqlite3
driver, built with -tags sqlite_fts5 and CGO_ENABLED=1. The deciding reason
recorded there was a viable path to in-process vectors via sqlite-vec (a C
loadable extension), which the pure-Go drivers cannot load. cgo carried a cost:
- The build needs a C toolchain (gcc), so
go install …@latestdoes not work on a bare Go install — the CI even had a "Verify toolchain" step asserting gcc. - Every
go build/test/vetinvocation has to carry-tags sqlite_fts5or FTS5 (keyword search) silently breaks; abuild_fts5_required.gotripwire existed only to fail the build when someone forgot the tag. - The container can't be
scratch/static-distroless; it has to ship glibc, and the binary links dynamically.
That deciding reason is now moot. 📝 ADR-0002 settled on
brute-force cosine in pure Go for vectors (no extension required), so the one
thing that needed a cgo driver — loading sqlite-vec — is no longer on the
roadmap. The remaining requirement from the data layer is just FTS5, which
the pure-Go drivers provide built in.
Decision
Switch the SQLite driver to modernc.org/sqlite (pure Go, FTS5 compiled in),
dropping cgo and the build tag entirely.
internal/store/store.goandinternal/store/migrations_test.goimportmodernc.org/sqliteandsql.Open("sqlite", dsn); the mattn import and thesqlite_fts5tag are gone.build_fts5_required.go(the tag tripwire) is deleted — FTS5 is unconditional now.- FTS5 was verified working under modernc:
CGO_ENABLED=0 go test ./...passes with no build tag, including the keyword-search tests ininternal/storeandinternal/web. - Build config is pinned cgo-off: the Makefile and CI set
CGO_ENABLED=0and carry no tag; the Dockerfile builds a fully static binary on agcr.io/distroless/static-debian12:nonrootbase.
go install github.com/joestump/msgbrowse/cmd/msgbrowse@latest becomes the
primary install path — it needs only a Go toolchain, no C compiler, no tag.
Docker remains a fully supported, optional path (the owner's explicit
decision): the image is now smaller and static, but make up and the compose
workflow are unchanged for users who prefer containers.
Consequences
- Good:
go installworks toolchain-free; the build is reproducible without gcc; the container is static and smaller (static-debian12, no glibc); no more build-tag footgun and no tripwire file to maintain; CI loses the gcc step. - Good: One driver, one DSN, FTS5 always on — the keyword-search path can no longer be silently disabled by a missing tag.
- Trade-off:
modernc.org/sqliteis a Go transpilation of SQLite, so it cannot load C loadable extensions (e.g.sqlite-vec). That door — left open by 📝 ADR-0001/0002 — is now closed. It is an acceptable loss because vectors are brute-force Go (📝 ADR-0002) and fast enough at single-user personal-archive scale. - Trade-off: Pure-Go SQLite can be modestly slower than the cgo amalgamation on heavy workloads. At this scale it is not felt; revisit only if the archive grows large enough to make query latency noticeable.
This ADR supersedes the driver/cgo choice in 📝 ADR-0001. The FTS5 requirement it established still holds; only the driver that satisfies it has changed.