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
Two build targets are available, both using SQLCipher v4.9.0 with OpenSSL 3.3.2 encryption:
| Target | Script | Shell | Emscripten | Output |
|---|---|---|---|---|
| WASM (Node.js/Browser) | build-wasm.sh |
nix develop |
Latest | dist/ + target/results/sqlcipher-wasm.zip |
| WebGL (Unity) | build-webgl.sh |
nix develop .#webgl |
3.1.10 (pinned for Unity 2022.3.22f) | target/results/sqlcipher-webgl.zip |
The WebGL build additionally enables SQLITE_ENABLE_SNAPSHOT and SQLITE_ENABLE_COLUMN_METADATA.
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'.
├── 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)
├── package.json # NPM package configuration
├── target/
│ ├── build/ # Intermediate build artifacts
│ │ ├── openssl-3.3.2/ # OpenSSL source (shared)
│ │ └── 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
├── 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
Compilation Flags (shared by both 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
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