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
-
Clone the repository
-
Enter the Nix environment:
nix develop
Or with direnv:
direnv allow
-
Build OpenSSL for WebAssembly (first time only):
./build-openssl.sh
-
Build SQLCipher WASM:
./build.sh
-
Run all tests:
npm test -
Run benchmarks:
npm run bench
.
├── flake.nix # Nix flake configuration
├── .envrc # direnv configuration
├── build-openssl.sh # OpenSSL build script
├── build.sh # SQLCipher WASM build script
├── package.json # NPM package configuration
├── dist/ # Output directory
│ ├── sqlcipher.js # JavaScript loader
│ └── 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 # Generate cross-platform test
└── docs/
└── archive/ # Historical documentation
Downloads and compiles OpenSSL 3.3.2 to WebAssembly:
- Configured for WASM target (
linux-generic32) - Optimized build (
-O3) - Disabled features: ASM, threads, engines, hardware acceleration
- Static library output
Compiles SQLCipher with OpenSSL:
- Copies SQLCipher source from Nix store
- Configures and creates amalgamation (
sqlite3.c) - Compiles with OpenSSL crypto provider
- Links with OpenSSL static libraries
- Outputs
sqlcipher.jsandsqlcipher.wasm
Key Compilation Flags:
SQLCipher flags:
SQLITE_HAS_CODEC- Enable encryptionSQLCIPHER_CRYPTO_OPENSSL- Use OpenSSL crypto providerSQLITE_TEMP_STORE=2- Use memory for temporary storageSQLITE_THREADSAFE=0- Disable threading (not needed in WASM)SQLITE_ENABLE_FTS5- Full-text searchSQLITE_ENABLE_RTREE- Spatial indexingSQLITE_ENABLE_JSON1- JSON support
Emscripten flags:
INITIAL_MEMORY=16MB- Starting memoryMAXIMUM_MEMORY=2GB- Maximum allowed memoryALLOW_MEMORY_GROWTH=1- Dynamic memory growthENVIRONMENT=node,web- Node.js and browser support
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', '[email protected]']
);
// 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:watch╔════════════════════════════════════════════════════════════╗
║ SQLCipher WASM Test Suite ║
╚════════════════════════════════════════════════════════════╝
Running 5 test suites...
▶ Running Unit Tests...
Core SQLCipher functionality tests
✓ Unit Tests completed in 542ms
▶ Running End-to-End Tests...
Complete workflow tests
✓ End-to-End Tests completed in 498ms
...
╔════════════════════════════════════════════════════════════╗
║ Test Summary ║
╚════════════════════════════════════════════════════════════╝
✓ PASS Unit Tests 542ms
✓ PASS End-to-End Tests 498ms
✓ PASS File Database Tests 523ms
✓ PASS Encryption Tests 445ms
✓ PASS Cross-Platform Tests 389ms
─────────────────────────────────────────────────────────────
ALL TESTS PASSED
Total: 5 Passed: 5 Failed: 0
Time: 2.40s
Run 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.
After changing build scripts:
./build.shClean rebuild:
rm -rf build/ dist/
./build.shRebuild OpenSSL (rarely needed):
rm -rf openssl-wasm/ openssl-3.3.2/
./build-openssl.shAdd 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
Both workflows:
- Cache OpenSSL and Emscripten for faster builds
- Build OpenSSL if not cached
- Build SQLCipher WASM
- Generate cross-platform test
- Run all test suites
- Run benchmarks (CI only)
Enter the Nix environment:
nix developRebuild OpenSSL:
rm -rf openssl-wasm/ openssl-3.3.2/
./build-openssl.shBuild first:
./build.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.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 sourceEM_CACHE- Emscripten cache directoryNODE_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