better-sqlite3-multiple-ciphers
Synchronous SQLite3 driver for Node.js, forked from better-sqlite3, with built-in multi-cipher database encryption support.
Repository Health
Technical Analysis
better-sqlite3-multiple-ciphers is a drop-in fork of better-sqlite3, the fastest and simplest synchronous SQLite3 binding for Node.js. It keeps the same native N-API addon architecture and synchronous API — prepare, run, get, all, iterate, transactions, custom functions and aggregates — while adding transparent database encryption backed by the SQLite3MultipleCiphers extension.
Encryption is applied through ordinary SQLite pragmas (key, rekey, cipher) rather than a separate API, so existing better-sqlite3 code can adopt at-rest encryption with minimal changes. It supports multiple cipher schemes including the modern sqleet default and legacy SQLCipher compatibility, letting encrypted databases interoperate with tools like DB Browser for SQLite and SQLiteStudio.
What You Get
- The full better-sqlite3 synchronous API (prepare/run/get/all/iterate, transactions, custom functions, aggregates, virtual tables, backups) with no behavioral changes for unencrypted use
- Transparent database encryption via SQLite3MultipleCiphers, selectable per-database through
PRAGMA key/rekey/cipherstatements or thekey()/rekey()methods - Support for legacy SQLCipher-compatible encryption (
cipher='sqlcipher',legacy=4) so databases stay readable in GUI tools like DB Browser for SQLite - Prebuilt native binaries for macOS, Linux (glibc and musl), and Windows on x64/arm64, with automatic fallback to a local node-gyp build
- 64-bit integer support and worker-thread support for large or slow queries, inherited from better-sqlite3
Common Use Cases
- Encrypting local application databases in Electron or desktop Node.js apps that store sensitive user data on disk
- Adding at-rest encryption to an existing better-sqlite3-based application with minimal code changes
- Shipping commercial/client-side software where the database file must not be readable if extracted from the device
- Migrating encrypted SQLCipher databases created by other tools into a Node.js application
- Replacing asynchronous SQLite drivers (node-sqlite3) with a faster synchronous one while still needing encryption
Under The Hood
Architecture
The library is a thin JavaScript wrapper (lib/database.js, lib/methods/*.js) over a native N-API C++ addon (src/addon.cpp, src/objects/{database,statement,backup,statement-iterator}.cpp) that statically links a vendored SQLite3MultipleCiphers build (deps/) together with the better-sqlite3 C++ core. lib/binding.js resolves the correct addon per platform/architecture, preferring a prebuilt binary and falling back to a local node-gyp compile, and lib/index.js composes the database factory with a dedicated SqliteError type. Every JS method — pragma.js, transaction.js, table.js, function.js, aggregate.js, backup.js, serialize.js — calls straight into the native handle synchronously rather than queuing callbacks or promises, so the design is a clean two-layer split along a JS-veneer/native-core seam: changes to native internals ripple across the entire lib/methods/* surface, while JS-side validation and option handling stay isolated from the engine itself.
Tech Stack
Built as a Node.js N-API addon via node-gyp/binding.gyp against the bundled SQLite3MultipleCiphers sources and node-addon-api (^8.0.0) for the C++/JS bridge. Prebuilt binaries are published per platform/architecture (darwin, linux, linuxmusl, win32 on x64/arm64) via dedicated lib/<platform>-<arch>.js entrypoints and a prebuilds/ directory, with build-release/build-debug npm scripts as a local fallback. TypeScript types ship via a DefinitelyTyped-derived index.d.ts. Dev/test tooling is mocha, chai, and fs-extra, with sqlite/sqlite3 present only as devDependencies for benchmark comparisons. CI spans five workflows — test.yml, prebuild-test.yml, prebuild-test-all.yml, build-and-publish.yml, and an update-sqlite3mc.yml workflow that periodically syncs the vendored cipher extension version.
Code Quality
34 mocha test files (test/00.setup.js through numbered suites in the 30s) mirror the public API one-to-one — database open/close/pragma/prepare/exec/explain, statement run/get/all/iterate/bind/columns, transactions, checkpoints, custom functions and aggregates — giving thorough coverage for a native addon. The JS layer favors explicit validation with descriptive TypeError/RangeError throws (constructor argument checks in database.js, option checks in pragma.js) over silent coercion, and no swallowed-error patterns were observed. There’s no dedicated linter config, but style (tabs, single quotes) is consistent throughout, and prebuild-test-all.yml runs the suite across a matrix of OS/architecture combinations before every publish, giving release-gated quality assurance.
What Makes It Unique
The project’s differentiator is transparent, multi-algorithm database encryption layered onto better-sqlite3 via the vendored SQLite3MultipleCiphers extension, exposed through ordinary PRAGMA key=/rekey= statements and key()/rekey() methods rather than a separate encrypted-database API — so consumers keep the exact same synchronous API and semantics (WAL mode, custom functions, transactions) while gaining at-rest encryption. It further supports legacy SQLCipher-compatible databases via a cipher='sqlcipher' pragma, preserving interoperability with GUI tools built around that format. The value is narrow and specific (encryption plumbing) rather than an architectural departure from better-sqlite3, which it otherwise reuses wholesale.