add-db-migration
Add a new database migration to EncryptedDb. Use when changing storage schema, data format, or serialization in the native library.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Add a new database migration to EncryptedDb. Use when changing storage schema, data format, or serialization in the native library.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
| name | add-db-migration |
| description | Add a new database migration to EncryptedDb. Use when changing storage schema, data format, or serialization in the native library. |
Guide for adding a new migration to rust/src/encrypted_db.rs. Migrations run automatically when EncryptedDb::open() is called.
Two separate version counters:
LATEST_SCHEMA_VERSION — data migrations (both platforms, always in sync)IDB_STRUCTURAL_VERSION — IDB object store changes (WASM only, bump separately)| Platform | Schema tracking | Migration atomicity |
|---|---|---|
| Native (SQLCipher) | db_meta.schema_version row | SQL transaction per migration |
| WASM (IndexedDB) | Encrypted WASM_META_KEY in mls_storage | IDB transaction per migration |
LATEST_SCHEMA_VERSION// rust/src/encrypted_db.rs
pub(crate) const LATEST_SCHEMA_VERSION: u32 = 2; // was 1
/// vN-1 -> vN: <description of what changes>.
fn migrate_native_v{N-1}_to_v{N}(conn: &rusqlite::Connection) -> Result<(), String> {
let tx = conn
.unchecked_transaction()
.map_err(|e| format!("Migration v{N-1}->v{N}: failed to begin transaction: {e}"))?;
// DDL changes:
// tx.execute_batch("ALTER TABLE mls_storage ADD COLUMN new_col BLOB;")?;
// Data transforms:
// let mut stmt = tx.prepare("SELECT key, value FROM mls_storage WHERE ...")?;
// ... transform and update rows ...
// Write new version (INSIDE the transaction = atomic).
tx.execute(
&format!("INSERT OR REPLACE INTO db_meta (key, value) VALUES ('{META_SCHEMA_VERSION}', '{N}')"),
[],
)
.map_err(|e| format!("Migration v{N-1}->v{N}: failed to write version: {e}"))?;
tx.commit()
.map_err(|e| format!("Migration v{N-1}->v{N}: commit failed: {e}"))?;
Ok(())
}
run_migrations()// In the native run_migrations():
if version < N {
Self::migrate_native_v{N-1}_to_v{N}(&conn)?;
}
/// vN-1 -> vN: <description of what changes>.
async fn migrate_wasm_v{N-1}_to_v{N}(&self) -> Result<(), String> {
// For data transforms, pre-encrypt all values BEFORE opening IDB transaction.
// IDB auto-commits when the event loop is idle (any .await kills the txn).
// Example: read all, transform, write back
// let all = self.idb_get_all().await?;
// let mut transformed = Vec::new();
// for (k, enc_v) in all {
// let v = wasm_decrypt(&self.key.0, &enc_v).await?;
// let new_v = transform(v);
// let enc_new_v = wasm_encrypt(&self.key.0, &new_v).await?;
// transformed.push((k, enc_new_v));
// }
// // Now open IDB transaction and write all at once
// ...
// Write new version.
self.idb_write_schema_version(N).await?;
Ok(())
}
run_migrations()// In the WASM run_migrations():
if version < N {
self.migrate_wasm_v{N-1}_to_v{N}().await?;
}
Also bump the structural version:
const IDB_STRUCTURAL_VERSION: u32 = 2; // was 1
And add to idb_ensure_stores():
if old_version < 2.0 {
db.create_object_store("new_store_name", params).unwrap();
}
In test/storage_test.dart:
test('schema_version returns expected value after migration', () async {
final engine = await createTestEngine();
expect(engine.schemaVersion(), N); // matches LATEST_SCHEMA_VERSION
});
Existing tests implicitly verify migration idempotency (every createTestEngine() runs migrations on a fresh DB).
make build # Rust compiles
make codegen # FRB bindings regenerate (if schema_version() signature changed)
make analyze # Clean analysis
make test # All tests pass
| Change | LATEST_SCHEMA_VERSION | IDB_STRUCTURAL_VERSION |
|---|---|---|
| New SQL column/table | Yes | No |
| Changed data serialization | Yes | No |
| Data restructuring (merge/split) | Yes | No |
| New IDB object store | Yes | Yes |
| Remove IDB object store | Yes | Yes |
| Bug fix (no data change) | No | No |
| New Rust API function | No | No |
if version < N. A DB at v1 upgrading to v3 runs v1->v2, then v2->v3.Wire uses the same approach (refinery for native SQL + custom WASM framework):
Our system is simpler (single KV table vs 18+ entity tables) but architecturally equivalent.
Release a new openmls_frb native crate version (stage 1 of the two-stage release). Use when the user wants to build/publish new native binaries after openmls dependency updates, bump the openmls_frb crate, or push a openmls_frb-* tag. NOT for the Dart pub.dev release (that is release-package).
Prepare a new version of openmls for publication to pub.dev. Use when user wants to release, publish, or tag a new version of the package.
Build openmls native libraries for different platforms. Use when user asks about building, compiling, or creating native libraries for iOS, Android, macOS, Linux, or Windows.
Update openmls native library version. Use when checking for updates, upgrading openmls, bumping version, or updating native dependencies.
Review openmls Dart code for security issues. Use when reviewing code changes, checking for proper API usage, verifying secure patterns, or auditing cryptographic code.
Flutter Rust Bridge patterns and best practices for this project. Use when writing Rust API code, adding new bindings, implementing MlsEngine methods, or troubleshooting FRB issues.