PouchDB Encryption Plugin
Crypto-Pouch is a PouchDB plugin designed to provide transparent, field-level encryption for document data stored in PouchDB and CouchDB databases. It currently operates at version 4.0.2 and integrates seamlessly with existing PouchDB instances. The plugin leverages the TweetNaCl.js library, an independently audited cryptographic library, utilizing the xsalsa20-poly1305 algorithm for robust authenticated encryption. Once initialized with a password, it transparently encrypts document contents on write operations and decrypts them on read operations, transforming the original document into a 'payload' property containing the ciphertext. A key differentiation is its explicit focus on document *contents*, leaving `_id`s, `_rev`s, and PouchDB view keys/values unencrypted. Attachments are also not encrypted by default, requiring careful consideration for applications needing full data at rest encryption. While a formal release cadence isn't published, updates are typically released as needed for maintenance or feature enhancements.
Common errors
-
Error: Could not decrypt document payload: MAC verification failed
cause The encrypted document's integrity check (Message Authentication Code - MAC) failed, likely because the document's `_id` was changed after encryption, or the document was manually moved between databases without PouchDB's replication.fixDo not modify the `_id` of encrypted documents after they are saved. For transferring encrypted documents between databases, always use PouchDB's native replication feature. -
TypeError: db.putAttachment is not a function
cause `crypto-pouch` overrides or disables the standard PouchDB `putAttachment` and `getAttachment` methods.fixTo manage attachments, embed them directly within the document object when using `db.put()` and retrieve them with `db.get()` by specifying `binary: true, attachment: true` in the options. -
Sensitive data appears unencrypted in view query results or `_all_docs`.
cause `crypto-pouch` specifically encrypts only document *contents* and explicitly does not encrypt `_id`, `_rev`, or any data emitted as keys/values from PouchDB view functions.fixRedesign view map functions to only emit non-sensitive or hashed data. For applications requiring complete data privacy at rest, evaluate alternative PouchDB plugins like ComDB or implement field-level hashing for view keys/values.
Warnings
- gotcha Attachments are not encrypted by `crypto-pouch` by default. If sensitive binary data needs encryption, it must be handled externally before being attached or an alternative solution employed.
- breaking `db.putAttachment` and `db.getAttachment` methods are not supported when `crypto-pouch` is active. Using these methods may lead to unexpected behavior or errors.
- gotcha Document `_id` and `_rev` fields, along with any keys or values emitted by PouchDB view functions, are not encrypted. These fields remain in plaintext within the database.
- gotcha Changing the `_id` of an encrypted document after it has been saved will cause decryption failures upon retrieval.
- gotcha Manually moving encrypted documents (e.g., by direct database manipulation or without PouchDB's replication mechanisms) between databases will prevent their correct decryption.
- gotcha The security of the encrypted data is directly dependent on the strength and confidentiality of the `password` provided to `db.crypto()`. Weak or compromised passwords render the encryption ineffective.
Install
-
npm install crypto-pouch -
yarn add crypto-pouch -
pnpm add crypto-pouch
Imports
- PouchDB
import PouchDB from 'pouchdb'
const PouchDB = require('pouchdb') - CryptoPouchPlugin
import { plugin } from 'crypto-pouch'; PouchDB.plugin(plugin);PouchDB.plugin(require('crypto-pouch')) - CryptoPouchTypes
import 'crypto-pouch'
Quickstart
const PouchDB = require('pouchdb');
PouchDB.plugin(require('crypto-pouch'));
async function setupEncryptedDb(dbName, password) {
const db = new PouchDB(dbName);
// Initialize encryption; documents will be transparently en/decrypted after this.
// Make sure to use a strong password for security.
await db.crypto(password).then(() => {
console.log('Database encryption enabled.');
});
// Example usage: putting and getting an encrypted document
const docId = 'mydoc';
const originalDoc = { _id: docId, message: 'hello world', sensitiveData: 'top secret' };
await db.put(originalDoc);
console.log('Document put:', originalDoc);
const fetchedDoc = await db.get(docId);
console.log('Document fetched (decrypted):', fetchedDoc);
// Showing a limitation: attachments are not encrypted by default.
// The README notes db.putAttachment/db.getAttachment are not supported.
// Instead, manage attachments directly within the document object:
await db.put({
_id: docId,
_attachments: {
'myAttachment.txt': {
content_type: 'text/plain',
data: Buffer.from('Hello, world! Attachment not encrypted.').toString('base64')
}
}
});
console.log('Document with unencrypted attachment put.');
// Remove encryption (forgets password, but existing docs remain encrypted)
// db.removeCrypto();
// console.log('Encryption removed.');
return db;
}
// Example execution (replace with your actual database name and password)
setupEncryptedDb('my_encrypted_db_quickstart', process.env.DB_PASSWORD ?? 'myStrongPassword123!')
.then(db => console.log('Setup complete for database:', db.name))
.catch(err => console.error('Error during quickstart setup:', err));