A production-ready WebAssembly build of SQLCipher with real OpenSSL-based encryption, high-level JavaScript API, and comprehensive test coverage.
- Real Encryption: Full SQLCipher encryption using OpenSSL 3.3.2 compiled to WebAssembly
- High-Level API: Easy-to-use JavaScript wrapper with automatic memory management
- Cross-Platform: Works in Node.js and browsers, compatible with native SQLCipher databases
- Comprehensive Tests: 5 test suites covering all functionality including cross-platform compatibility
- Nix Flake Environment: Reproducible development environment
- Type Definitions: TypeScript definitions included
- Benchmarked: Performance benchmarks for critical operations
This build uses real cryptographic primitives from OpenSSL 3.3.2, providing:
- AES-256-CBC encryption
- PBKDF2-HMAC-SHA1 key derivation (64,000 iterations by default)
- HMAC-SHA1 for authentication
- Cryptographically secure random number generation
Databases created with this library are fully compatible with native SQLCipher (C++ version).
Add to your ~/.config/nix/nix.conf (or /etc/nix/nix.conf):
experimental-features = nix-command flakes
Three build targets are available, all using SQLCipher v4.9.0:
| Target | Script(s) | Shell | Emscripten | Crypto | Output |
|---|---|---|---|---|---|
| WASM (Node.js/Browser) | build-wasm.sh |
nix develop |
Latest (in-tree) | OpenSSL 3.3.2 | dist/ + target/results/sqlcipher-wasm.zip |
| WebGL (Unity 2022.3.22f) | build-webgl.sh |
nix develop .#webgl |
3.1.10 (pinned) | OpenSSL 3.3.2 | target/results/sqlcipher-webgl.zip |
| Amalgamation (Unity, any emcc) | build-libtomcrypt.sh → build-amalgamation.sh |
nix develop |
n/a (source bundle) | libtomcrypt 1.18.2 | target/results/sqlcipher-amalgamation.zip |
The WebGL build additionally enables SQLITE_ENABLE_SNAPSHOT and SQLITE_ENABLE_COLUMN_METADATA.
The WebGL target above emits a prebuilt libsqlcipher.a that must ABI-match the emcc Unity ships with. That's fine for a single pinned Unity version but breaks across upgrades: a .a built with emcc 3.1.10 (Unity 2022.3.22f) linked into Unity 6000.4 (emcc 3.1.38) traps at runtime with signature_mismatch:time — libc signatures like time_t drifted between emcc versions.
The Amalgamation target sidesteps this entirely: it ships portable C source (one sqlite3.c, one libtomcrypt.c, and a small set of headers) that Unity's own bundled emcc compiles in-tree, so the same bundle works across 2022.3, 6000.x, and future Unity versions with no toolchain pin.
Crypto is libtomcrypt instead of OpenSSL because OpenSSL has no usable source-drop path (Perl-driven Configure, generated opensslconf.h, hundreds of per-file -D flags). libtomcrypt amalgamates cleanly into a single .c once curated to the subset SQLCipher actually calls (AES, SHA1/2, HMAC, CBC/ECB, PBKDF2, PRNG, registration helpers).
Interactive:
nix develop
./build-openssl.sh
./build-wasm.sh
npm testOne-liner:
nix develop --command bash -c './build-openssl.sh && ./build-wasm.sh'Interactive:
nix develop .#webgl
./build-openssl.sh
./build-webgl.shOne-liner:
nix develop .#webgl --command bash -c './build-openssl.sh && ./build-webgl.sh'Interactive:
nix develop
./build-libtomcrypt.sh
./build-amalgamation.shOne-liner:
nix develop --command bash -c './build-libtomcrypt.sh && ./build-amalgamation.sh'The output target/results/sqlcipher-amalgamation/ is a flat drop-in directory:
sqlite3.c SQLCipher amalgamation (compile flags baked in at top)
sqlite3.h SQLCipher public header
libtomcrypt.c curated libtomcrypt amalgamation
tomcrypt*.h 13 libtomcrypt headers (angled includes rewritten to quoted)
LICENSE_libtomcrypt libtomcrypt license
README.txt Unity integration notes
Copy the directory into Assets/Plugins/WebGL/sqlcipher/ — that's it. No plugin importer compile flags, no include paths, no force-includes. Every -D and -I that would normally be needed is either prepended into sqlite3.c itself or obviated by the flat layout (quoted sibling includes resolve via clang's current-file-directory search). The same bundle compiles unchanged on Unity 2022.3 (emcc 3.1.10), Unity 6000.4 (emcc 3.1.38), and whatever emcc the next Unity ships.
.
├── flake.nix # Nix flake (default + webgl shells)
├── .envrc # direnv configuration
├── lib.sh # Shared build utilities (includes patch_amalgamation)
├── build-openssl.sh # OpenSSL WASM build script
├── build-wasm.sh # SQLCipher WASM build (CJS/ESM)
├── build-webgl.sh # SQLCipher static lib build (Unity WebGL, emcc-pinned)
├── build-libtomcrypt.sh # libtomcrypt curated-amalgamation stage (no emcc)
├── build-amalgamation.sh # SQLCipher + libtomcrypt source bundle (Unity, any emcc)
├── package.json # NPM package configuration
├── target/
│ ├── build/ # Intermediate build artifacts
│ │ ├── openssl-3.3.2/ # OpenSSL source (shared)
│ │ ├── libtomcrypt-1.18.2/ # libtomcrypt source (shared)
│ │ ├── libtomcrypt-amalg/ # Curated libtomcrypt amalgamation output
│ │ ├── amalgamation/ # SQLCipher amalgamation working dir
│ │ └── emcc-<version>/ # Per-emscripten-version builds
│ │ ├── openssl-wasm/ # Compiled OpenSSL
│ │ ├── sqlcipher-wasm/ # WASM build working dir
│ │ └── sqlcipher-webgl/ # WebGL build working dir
│ └── results/ # Final build outputs
│ ├── sqlcipher-wasm/ # WASM artifacts (unpacked)
│ ├── sqlcipher-wasm.zip # WASM archive
│ ├── sqlcipher-webgl/ # WebGL artifacts (unpacked)
│ │ ├── libsqlcipher.a
│ │ └── openssl/
│ │ ├── libcrypto.a
│ │ └── libssl.a
│ ├── sqlcipher-webgl.zip # WebGL archive
│ ├── sqlcipher-amalgamation/ # Source bundle (unpacked)
│ └── sqlcipher-amalgamation.zip # Source bundle archive
├── dist/ # NPM package output
│ ├── sqlcipher.cjs # CommonJS module (Node.js)
│ ├── sqlcipher.mjs # ES module (browser)
│ └── sqlcipher.wasm # WebAssembly binary
├── lib/
│ └── sqlite-api.cjs # High-level JavaScript API
├── test/
│ ├── run-all-tests.cjs # Test suite runner
│ ├── test.cjs # Core functionality tests
│ ├── e2e-test.cjs # End-to-end tests
│ ├── file-db-test.cjs # File persistence tests
│ ├── encryption-test.cjs # Encryption tests
│ └── cross-platform-db-test.cjs # C++ <-> WASM compatibility (generated)
├── bench/
│ └── benchmark.cjs # Performance benchmarks
├── examples/
│ └── example.cjs # Usage examples
├── tools/
│ └── prepare-cross-platform-test.sh
└── docs/
└── archive/
All build scripts source lib.sh which auto-detects the emscripten version and sets up versioned build/cache directories. This allows the default and webgl shells to coexist without conflicts.
Downloads and compiles OpenSSL 3.3.2 to WebAssembly:
- Configured for WASM target (
linux-generic32) - Optimized build (
-O3 -flto) - Disabled features: ASM, threads, engines, hardware acceleration
- Static library output to
target/build/emcc-<version>/openssl-wasm/ - OpenSSL source tarball is shared across emscripten versions
Compiles SQLCipher with OpenSSL into CJS/ESM modules:
- Copies SQLCipher v4.9.0 source from Nix store
- Configures and creates amalgamation (
sqlite3.c) - Patches amalgamation to use OpenSSL crypto provider
- Compiles and links with OpenSSL static libraries
- Outputs to
dist/(for npm) andtarget/results/sqlcipher-wasm/ - Creates
target/results/sqlcipher-wasm.zip
Compiles SQLCipher into a static library for Unity 2022.3.22f WebGL:
- Same amalgamation process as WASM build
- Compiles with emscripten 3.1.10 (ABI-compatible with Unity's bundled emscripten)
- Outputs
libsqlcipher.a+ OpenSSL libs totarget/results/sqlcipher-webgl/ - Creates
target/results/sqlcipher-webgl.zip
Prepares a curated libtomcrypt amalgamation consumed by build-amalgamation.sh. This is a source-transform stage — no emcc is invoked.
- Downloads libtomcrypt 1.18.2 tarball (shared across runs)
- Enumerates the subset SQLCipher calls: AES, SHA1/2 family, HMAC, CBC/ECB modes, PBKDF2 (PKCS#5 alg2), PRNGs, registration/argchk helpers
- Inlines
aes_tab.cintoaes.cso the amalgamation has no side-file dependency - Concatenates the subset into
target/build/libtomcrypt-amalg/libtomcrypt.c - Copies the 13 upstream headers to
target/build/libtomcrypt-amalg/headers/ - Smoke-tests the result compiles cleanly with host
cc
Unrelated algorithms (Blowfish, Camellia, Khazad, MDx, RIPEMD, etc.) are intentionally omitted — they collide on static symbols (T-tables, per-algo F/G macros) when amalgamated, and SQLCipher never touches them.
Assembles the SQLCipher + libtomcrypt source bundle consumable by Unity's own emcc, so the same output works across Unity versions without ABI pinning.
- Asserts
build-libtomcrypt.shhas been run (its output is intarget/build/libtomcrypt-amalg/) - Builds the SQLCipher amalgamation (
sqlite3.c+sqlite3.h) via the standard./configure && make sqlite3.cflow — still no emcc - Patches
sqlite3.cto activate the libtomcrypt provider and disable OpenSSL/NSS/CC - Copies the prebuilt
libtomcrypt.cand headers alongside - Prepends the SQLCipher/SQLite
#defineblock directly to the top of the bundledsqlite3.cso no-Dflags are ever needed at compile time - Rewrites
#include <tomcrypt.h>(in sqlite3.c's crypto_libtomcrypt section) to"tomcrypt.h"so it resolves from the plugin's own directory without an-Ipath - Flattens all libtomcrypt headers alongside
libtomcrypt.cin the bundle root so quote-include lookups succeed out of the box - Creates
target/results/sqlcipher-amalgamation.zip
Compilation Flags (shared by WASM and WebGL builds):
SQLITE_HAS_CODEC- Enable encryptionSQLCIPHER_CRYPTO_OPENSSL- Use OpenSSL crypto providerSQLITE_TEMP_STORE=2- Use memory for temporary storageSQLITE_THREADSAFE=1- Thread-safe (required by SQLCipher v4.9.0)SQLITE_ENABLE_FTS5- Full-text searchSQLITE_ENABLE_RTREE- Spatial indexingSQLITE_ENABLE_JSON1- JSON support
WebGL build adds:
SQLITE_ENABLE_SNAPSHOT- Database snapshot supportSQLITE_ENABLE_COLUMN_METADATA- Column metadata API
Amalgamation bundle substitutes the OpenSSL provider with libtomcrypt:
SQLCIPHER_CRYPTO_LIBTOMCRYPTreplacesSQLCIPHER_CRYPTO_OPENSSLLTC_SOURCEenables libtomcrypt internal symbols- every flag is prepended directly into
sqlite3.c/libtomcrypt.cat bundle assembly time — no external-Dor force-include header needed
const { SQLiteAPI } = require('./lib/sqlite-api.cjs');
const initSqlcipher = require('./dist/sqlcipher.js');
async function main() {
// Initialize the WASM module
const Module = await initSqlcipher();
const sqlite = new SQLiteAPI(Module);
// Create an encrypted database
const db = sqlite.open('/mydb.db', 'my-secret-password');
// Create a table
db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)');
// Insert data
db.exec(
'INSERT INTO users (name, email) VALUES (?, ?)',
['Alice', 'alice@example.com']
);
// Query data
const users = db.query('SELECT * FROM users WHERE name = ?', ['Alice']);
console.log(users);
// => [{ id: 1, name: 'Alice', email: 'alice@example.com' }]
// Close database
db.close();
}
main();// Open with password
const db = sqlite.open('/encrypted.db', 'password123');
// Change password (re-key)
db.exec("PRAGMA rekey = 'new-password'");
// Multiple databases with different passwords
const db1 = sqlite.open('/db1.db', 'password1');
const db2 = sqlite.open('/db2.db', 'password2');
// Check encryption worked
db1.exec('SELECT count(*) FROM sqlite_master'); // OK
db2.exec('SELECT count(*) FROM sqlite_master'); // OK<script type="module">
import initSqlcipher from './dist/sqlcipher.js';
import { SQLiteAPI } from './lib/sqlite-api.js';
const Module = await initSqlcipher();
const sqlite = new SQLiteAPI(Module);
const db = sqlite.open('/mydb.db', 'secret');
console.log('SQLCipher ready!');
</script>High-level JavaScript API for SQLCipher.
Open or create a database.
path- Database file path (e.g.,/mydb.db)password- Encryption password (optional for unencrypted databases)- Returns:
Databaseinstance
Execute SQL statement.
sql- SQL statementparams- Optional array of parameters for?placeholders- Returns:
void
Execute query and return results.
sql- SQL queryparams- Optional array of parameters- Returns: Array of result objects
Close the database connection.
Get number of rows changed by last statement.
- Returns:
number
The test suite includes 5 comprehensive test suites:
- Module loading and initialization
- Basic SQL operations
- Memory management
- API correctness
- Complete workflows
- Multi-step operations
- Error handling
- Edge cases
- File persistence
- VFS (Virtual File System)
- Database reopening
- File operations
- PRAGMA key interface
- Password verification
- Multiple databases with different passwords
- Database re-keying
- Wrong password handling
- API key vs PRAGMA key equivalence
- C++ (native SQLCipher) -> WASM compatibility
- Database created with native SQLCipher, read by WASM
- Binary compatibility verification
- Real-world migration scenarios
Run all tests:
npm testRun tests in watch mode:
npm run test:watchRun performance benchmarks:
npm run benchBenchmarks measure:
- Module initialization time
- Memory allocation performance
- Database operations (INSERT, SELECT, UPDATE, DELETE)
- Query performance
- Encryption overhead
- Memory usage patterns
Databases created with this WASM build are 100% compatible with native SQLCipher.
Generate and run the cross-platform test:
./tools/prepare-cross-platform-test.sh
node test/cross-platform-db-test.cjsThis creates a database with native SQLCipher (C++), encodes it, and verifies WASM can read it.
Just copy your .db file and open it with the same password:
const db = sqlite.open('/path/to/existing.db', 'same-password');
const data = db.query('SELECT * FROM your_table');The reverse also works - databases created in WASM can be used in native applications.
Clean rebuild (WASM):
rm -rf target/ dist/
./build-openssl.sh && ./build-wasm.shClean rebuild (WebGL):
rm -rf target/
./build-openssl.sh && ./build-webgl.shFull clean:
rm -rf target/ dist/Add to appropriate test file in test/:
test('Your test name', () => {
const db = sqlite.open('/test.db', 'password');
db.exec('CREATE TABLE test (id INTEGER)');
const result = db.query('SELECT * FROM test');
assert.strictEqual(result.length, 0);
db.close();
});Tests are automatically picked up by npm test.
GitHub Actions workflows:
- CI/CD (
.github/workflows/ci.yml) - Build, test, and publish on releases - PR Checks (
.github/workflows/pr-check.yml) - Quick validation on pull requests
CI pipeline:
- Build OpenSSL + SQLCipher WASM (
nix develop) - Build OpenSSL + SQLCipher WebGL (
nix develop .#webgl) - Run tests and benchmarks
- Upload
sqlcipher-wasm.zipandsqlcipher-webgl.zipas artifacts - Publish to NPM and create GitHub Release on tags
Enter the Nix environment:
nix developRebuild OpenSSL:
rm -rf target/build/
./build-openssl.shBuild first:
./build-openssl.sh && ./build-wasm.shWrong password or corrupted database. Verify password:
try {
const db = sqlite.open('/test.db', 'password');
db.query('SELECT * FROM sqlite_master'); // Will fail if wrong password
} catch (err) {
console.log('Wrong password or corrupted database');
}Increase Node.js memory:
export NODE_OPTIONS="--max-old-space-size=4096"
./build-wasm.shMake sure native SQLCipher is available (provided by Nix environment):
nix develop --command bash -c "which sqlcipher"- Batch operations - Use transactions for multiple inserts
- Prepare statements - Reuse prepared statements for repeated queries
- Appropriate memory settings - Adjust
PRAGMA cache_size - Index wisely - Create indexes for frequently queried columns
- Use the benchmarks - Profile before optimizing
Example batch insert:
db.exec('BEGIN TRANSACTION');
for (let i = 0; i < 1000; i++) {
db.exec('INSERT INTO users (name) VALUES (?)', [`User ${i}`]);
}
db.exec('COMMIT');Contributions welcome! Areas for improvement:
- Browser-based test runner
- More comprehensive benchmarks
- IndexedDB persistence examples
- Worker thread examples
- React/Vue/Svelte integration examples
- Performance optimization guides
The package is configured for NPM publishing:
npm version patch # or minor, major
git push --tagsCreate a GitHub release to trigger automatic publishing to:
- NPM registry
- GitHub Packages
In the Nix environment:
SQLCIPHER_SRC- Path to SQLCipher source (set by flake.nix)EM_CACHE- Emscripten cache directory (set by lib.sh per emcc version)NODE_PATH- Node.js module search path
- SQLCipher Documentation
- OpenSSL Documentation
- Emscripten Documentation
- SQLite Documentation
- WebAssembly MDN
MIT License - See LICENSE file for details
Dependencies:
- SQLCipher - BSD-style license
- SQLite - Public domain
- OpenSSL - Apache License 2.0
- Emscripten - MIT/NCSA License
- SQLCipher team for the encrypted SQLite fork
- OpenSSL team for the cryptographic library
- Emscripten team for the WASM toolchain
- SQLite team for the amazing database engine
- Nix community for reproducible builds