mongoose-aes-encryption-migrate
v1.0.0
Published
CLI migration tool to encrypt existing MongoDB collections for use with mongoose-aes-encryption
Maintainers
Readme
mongoose-aes-encryption-migrate
CLI migration tool for mongoose-aes-encryption for existing databases in plaintext or using other Mongoose encryption plugins.
Supports migration for the following paths:
| Mode | Source plugin | Source encryption |
|---|---|---|
| plaintext | No encryption — plain text fields | none |
| mongoose-encryption | mongoose-encryption | AES-CBC |
| mongoose-field-encryption | mongoose-field-encryption | AES-CBC |
Always back up your database before running a migration.
Installation
npm install -g mongoose-aes-encryption-migrateOr use without installing via npx:
npx mongoose-aes-encryption-migrate --helpCLI usage
Mode 1 — Plain-text to encrypted
Use this when you have added encrypted: true to fields in an existing Mongoose schema and need to encrypt all existing documents that still hold plain-text values.
mongoose-aes-encryption-migrate \
--uri mongodb://localhost:27017/mydb \
--collection users \
--mode plaintext \
--key 9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f \
--fields name,email,salaryThe tool is idempotent — documents whose fields are already encrypted are detected and skipped automatically. Safe to re-run.
Mode 2 — From mongoose-encryption
Use this when migrating from mongoose-encryption, which stores all encrypted fields bundled together in a single _ct Binary field per document.
mongoose-aes-encryption-migrate \
--uri mongodb://localhost:27017/mydb \
--collection users \
--mode mongoose-encryption \
--key 9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f \
--source-key <base64-encoded encryptionKey from mongoose-encryption> \
--fields name,email \
--plaintext-fields salaryImportant: mongoose-encryption bundles multiple fields into a single _ct blob. Every field found inside that blob must be explicitly accounted for:
--fields— fields to re-encrypt individually withmongoose-aes-encryption--plaintext-fields— fields to restore as unencrypted plaintext
If any field inside _ct is not covered by either flag, the tool aborts at pre-flight and lists the unaccounted fields along with suggested corrected commands. No data is lost or changed.
After migration, _ct and _ac are removed from every document.
Mode 3 — From mongoose-field-encryption
Use this when migrating from mongoose-field-encryption, which stores each field as a per-field AES-256-CBC string in the format <salt-hex>:<ciphertext-hex> alongside __enc_<field> boolean marker fields.
mongoose-aes-encryption-migrate \
--uri mongodb://localhost:27017/mydb \
--collection users \
--mode mongoose-field-encryption \
--key 9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f \
--secret "the secret string used with mongoose-field-encryption" \
--fields name,email,salaryAfter migration, the __enc_<field> boolean markers and any __enc_<field>_d data fields (used for non-string types) are removed from every document.
Documents where the __enc_<field> marker is false or absent are skipped (field is already plaintext).
All options
| Option | Required | Default | Description |
|---|---|---|---|
| --uri | yes | — | MongoDB connection string, must include database name |
| --collection | yes | — | Collection name to migrate |
| --mode | yes | — | plaintext, mongoose-encryption, or mongoose-field-encryption |
| --key | yes | — | 64-character hex target encryption key |
| --fields | yes | — | Comma-separated field paths to encrypt |
| --plaintext-fields | mode 2 only | — | Comma-separated fields from _ct to restore as plaintext |
| --source-key | mode 2 only | — | base64 encryptionKey used with mongoose-encryption |
| --secret | mode 3 only | — | Secret string used with mongoose-field-encryption |
| --algorithm | no | aes-256-gcm | Target algorithm: aes-256-gcm or aes-256-cbc |
| --batch-size | no | 100 | Number of documents to process per batch |
| --dry-run | no | false | Probe and report without writing any changes |
When running without
--dry-run, the tool will prompt for confirmation before writing any changes. Answeryto proceed. Default answer isn(abort).
Dry-run mode
Add --dry-run to any command to see what would happen without touching the database:
mongoose-aes-encryption-migrate \
--uri mongodb://localhost:27017/mydb \
--collection users \
--mode plaintext \
--key 9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f \
--fields name,email \
--dry-runError handling
If a document fails to update (e.g. due to a write conflict), the tool pauses and asks:
Error processing document _id=64a3f...: <error message>
? What do you want to do?
> Skip this document and continue
Abort the migrationSkipped document IDs are reported in the final summary. You can re-run the tool afterward — already-migrated documents are skipped automatically.
Programmatic API
For use in custom migration scripts or CI pipelines. Errors throw immediately (no interactive prompts).
const {
plaintextToEncrypted,
mongooseEncryptionToEncrypted,
mongooseFieldEncryptionToEncrypted
} = require('mongoose-aes-encryption-migrate');
// Mode 1 — plain text
const result = await plaintextToEncrypted({
uri: 'mongodb://localhost:27017/mydb',
collection: 'users',
fields: ['name', 'email', 'salary'],
key: '9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f',
algorithm: 'aes-256-gcm', // optional, default
batchSize: 100, // optional, default
dryRun: false // optional, default
});
// result: { migrated: 998, skipped: 2, errors: 0 }
// Mode 2 — mongoose-encryption
const result2 = await mongooseEncryptionToEncrypted({
uri: 'mongodb://localhost:27017/mydb',
collection: 'users',
fields: ['name', 'email'],
plaintextFields: ['salary'],
key: '9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f',
sourceKey: '<base64 encryptionKey>',
dryRun: false
});
// Mode 3 — mongoose-field-encryption
const result3 = await mongooseFieldEncryptionToEncrypted({
uri: 'mongodb://localhost:27017/mydb',
collection: 'users',
fields: ['name', 'email', 'salary'],
secret: 'the secret string used with mongoose-field-encryption',
key: '9af7d400be4705147dc724db25bfd2513aa11d6013d7bf7bdb2bfe050593bd0f',
dryRun: false
});All functions return Promise<{ migrated: number, skipped: number, errors: number }>.
How each source plugin is migrated
mongoose-encryption
mongoose-encryption stores all encrypted fields together in a single _ct BSON Binary field:
_ct layout: [ version (1 byte) | IV (16 bytes) | AES-256-CBC ciphertext ]
ciphertext decrypts to: JSON.stringify({ name: "Joe", email: "...", salary: 50000 })The migration tool:
- Decrypts
_ctusing the AES-256-CBCencryptionKeyprovided as--source-key(base64) - Re-encrypts each field in
--fieldsindividually usingmongoose-aes-encryption's wire format - Restores fields in
--plaintext-fieldsas unencrypted values - Writes
$setof all new field values and$unsetof_ctand_acin a single atomic update per document
Documents without a _ct field are skipped (already migrated or never encrypted).
mongoose-field-encryption
mongoose-field-encryption stores each encrypted field in-place as:
<16-byte-salt-hex>:<ciphertext-hex>Using AES-256-CBC with a key derived from the user's secret via SHA-256(secret).slice(0, 32 bytes). A boolean marker __enc_<fieldname>: true marks encrypted fields. Non-string types are additionally stored in a __enc_<fieldname>_d field with the original field set to undefined.
The migration tool:
- Checks the
__enc_<field>marker — iffalseor absent, the field is plaintext and skipped - Derives the AES key from the
--secretusing the same SHA-256 method - Decrypts each field value (or
__enc_<field>_dfor non-string types) - Re-encrypts with
mongoose-aes-encryption's wire format - Writes
$setof new ciphertext values and$unsetof all__enc_*marker and data fields
Note on non-string field types: mongoose-field-encryption stores non-string values (numbers, booleans, dates) as JSON.stringify'd strings in a separate __enc_<field>_d field. After migration, these values are stored as encrypted strings. If your Mongoose schema defines those fields with a non-string type (e.g. Number, Boolean, Date), Mongoose will automatically cast the decrypted string back to the correct type on read. If you access the collection via the raw MongoDB driver without Mongoose, you will need to cast the values manually after decryption.
License
MIT
