Tracks each sub-package under vfs/. The root vfs/ package
offers two surfaces: it re-exports modernc.org/sqlite/vfs so any
fs.FS becomes a read-only SQLite VFS, and it exposes a public
user-implementable VFS / File interface (vfs.Register) so
downstream code can back a writable database with arbitrary Go storage
through one generic dispatcher. Everything else wraps a base VFS to add a
specific capability (encryption, checksum) by intercepting xRead /
xWrite.
Status legend:
- ✓ landed — code + tests + docs shipped
- ⚠ partial — code shipped but coverage incomplete, or a feature gated
- ✗ deferred — analyzed and chosen for a later round
- ✗ skipped — analyzed and intentionally dropped (overlap with existing surface)
| sub-package | Upstream | Status | Entry | Test pin |
|---|---|---|---|---|
vfs/ (root, fs.FS adapter) |
ncruces vfs/readervfs | ✓ landed | vfs.New(fs.FS) |
vfs/vfs_test.go |
vfs/ (root, user-implementable VFS) |
ncruces vfs core | ✓ landed (rollback-journal + WAL via ShmFile; in-process shm) |
vfs.Register(name, VFS) + vfs.VFS / vfs.File / vfs.ShmFile interfaces |
vfs/interface_test.go, vfs/shm_test.go |
vfs/crypto |
ncruces vfs/adiantum + vfs/xts | ✓ landed | crypto.New(Options) |
vfs/crypto/integration_test.go |
vfs/cksm |
ncruces vfs/cksm.go | ✓ landed | cksm.New(Options) + (*sqlite.Conn).EnableChecksums(schema) |
vfs/cksm/cksm_test.go |
vfs/mvcc |
ncruces vfs/mvcc | ✓ landed (Go-native re-implementation, no wbt dep) | mvcc.New(Options) |
vfs/mvcc/mvcc_test.go |
vfs/memdb |
ncruces vfs/memdb | ✓ landed | memdb.New(Options) |
vfs/memdb/memdb_test.go |
vfs.NewReader(io.ReaderAt, size) |
ncruces vfs/readervfs | ✓ landed | vfs.NewReader(r, n) |
TestVFS_OpenFromReaderAt, TestVFS_NewReader_NilRejected, TestVFS_NewReader_NegativeSizeRejected (vfs/vfs_test.go) |
vfs/crypto covers both ncruces sub-packages: pick between Adiantum
(default, 32-byte key, length-preserving wide-block cipher) and
AES-XTS-256 (64-byte key, mandated by some compliance regimes) via the
Options.Cipher field. The cipher's tweak is domain-separated by file
kind (main DB / journal / WAL / temp DB / temp / sub-journal) so the
same plaintext at the same page number in different file kinds
produces distinct ciphertext. Encryption-at-rest covers everything that
hits xRead/xWrite; the WAL -shm index file is plaintext because it is
process-local coordination state, not row data.
vfs/crypto is confidentiality-only with a single raw Options.Key.
Multi-recipient encryption (a wrapped data key, no shared secret), crash-safe key
rotation, tamper-evident storage (per-page integrity + a signed root), and
optional rollback resistance via an external anchor live in
gosqlite.org/vfs/vault, a self-contained container that keeps the keyslot inside
the file and where compression and encryption are independent options.
vfs/crypto and vfs/cksm both accept an Options.WrapVFS string
field that names another registered VFS to layer on top of (rather
than the system default). The canonical stack is checksum-on-the-
inside / encrypt-on-the-outside:
cksmName, cksmFS, _ := cksm.New(cksm.Options{})
defer cksmFS.Close()
cryptoName, cryptoFS, _ := crypto.New(crypto.Options{
Key: key,
WrapVFS: cksmName, // layer cksm beneath crypto
})
defer cryptoFS.Close()
sql.Open("sqlite", "file:db.db?vfs="+cryptoName)On write, crypto encrypts and forwards to cksm, which stamps the
checksum trailer over the ciphertext and forwards to the system
default. On read, the order reverses: cksm verifies first (proving
the ciphertext arrived intact), then crypto decrypts. Per-file state
in each layer lives at the wrapped VFS's szOsFile offset, so the
layers don't collide.
vfs/cksm is a page-level checksum VFS — every main-DB page write
computes a 32-bit Fletcher-style rolling sum over the page body and
stamps the 8-byte trailer; every read verifies it. Activates only when
the database's reserved_bytes byte (offset 20 of the SQLite header)
reads 8, which (*sqlite.Conn).EnableChecksums(schema) sets via
SQLITE_FCNTL_RESERVE_BYTES and then VACUUMs the database to rewrite
every existing page with the trailer in place.
| feature | Reason |
|---|---|
ncruces vfs/ core framework (api.go, file.go, lock_.go, os_.go, shm_*.go) |
Specific to ncruces' Wazero-WASM runtime. We use modernc.org/sqlite/vfs, whose core framework is the transpiled SQLite default VFS — different runtime, same end behaviour. |
vfs.Register(name, impl) drives any implementation of the public
vfs.VFS (Open/Delete/Access/FullPathname) and vfs.File
(ReadAt/WriteAt/Truncate/Sync/Size/Lock/Unlock/CheckReservedLock/
SectorSize/DeviceCharacteristics/Close) interfaces through one shared
set of generic trampolines — the same internal/cabi registry +
function-pointer machinery the bespoke sub-packages use, lifted to
dispatch into a Go interface. Embed vfs.NoLock for accept-everything
advisory locking (the single-process case); return a *vfs.VFSError to
surface a specific SQLITE_* result code; implement the optional
vfs.FileControl capability interface to handle file-control opcodes.
The dispatcher copies every buffer at the C boundary, delegates
clock/sleep/randomness to the platform VFS, and refuses Unregister
while any database is still open.
vfs.Wrap(base, recorder) (Phase 3) decorates any VFS so each
Open/Read/Write/Sync reports latency + byte count + error to a
vfs.Recorder; vfs.NewSlogRecorder is the built-in log/slog Recorder
(Debug per op, Warn on a genuine fault, with the expected io.EOF
short-read carved out). A nil Recorder returns the base unchanged. The
wrapper forwards the optional vfs.FileControl capability so wrapping
never silently drops it. Test pin: vfs/wrap_test.go.
WAL is supported through the optional vfs.ShmFile capability: a File
that also implements ShmGroup() string advertises the xShm* methods,
so SQLite offers it WAL mode. The dispatcher owns the shared-memory
regions (stable C allocations the WAL index lives in) and the 8-slot WAL
lock table — user code only declares which open files share a WAL index
(same ShmGroup key → shared shm). Coordination is in-process: it backs
multiple database/sql connections to one Go-managed database within a
process, not cross-process WAL over a real filesystem (the platform VFS
remains the tool for that). A File that does not implement ShmFile
stays on the iVersion 1 methods table and runs in rollback-journal
mode. Test pin: vfs/shm_test.go (WAL round-trip, shared-reader
visibility, and a 1-writer/4-reader concurrent stress under -race).
The reference refMemVFS in vfs/interface_test.go is a complete
writable in-memory VFS on this interface and doubles as a copy-paste
template; examples/features/vfs/custom/ is the runnable version (with vfs.Wrap
instrumentation).
This is the from-scratch path; the wrap-and-forward sub-packages below (crypto/cksm) layer over an existing VFS instead and keep their bespoke trampolines.
- Pick an upstream pattern from the ncruces/go-sqlite3 vfs/ tree or the SQLite extensions registry.
- Mirror the
vfs/cryptolayout:cksm.go-styleNew/Close/FS,vfs.go-styleperFileState+xOpentrampoline,iomethods.go-style 12 io-method trampolines pluscallXFoowrappers, optionalderive_*.goorcipher.gofor any helpers. - Use
internal/cabi.FuncPointerfor Go→C function-pointer slots andinternal/cabi.AsFunc[F]for C→Go reads back from stored uintptrs, or the typedcabi.CallX*family for io-method slot dispatch. (All three are the same pattern asvfs/crypto.) - Add a row to the table above; flip to ✓ landed once tests + lint pass.
- Add/update the matching
skills/entry and the relevantdocs/page so consumer agents and humans find it. - Optional: drop a runnable example under
examples/vfs-<name>/.
Last reviewed against ncruces/go-sqlite3 main on 2026-06-13.