v0.0.38: FC-P4 complete — session versioning, wire envelope, auto-backup

FC-P4-T1: Session State Versioning
- RatchetState serialize/deserialize with [MAGIC:0xFC][VERSION:1][bincode]
- Legacy (raw bincode) still loads — backward compatible
- Client + WASM both use versioned format
- 2 new tests: roundtrip + legacy compat

FC-P4-T2: WireMessage Versioning Envelope
- Format: [WZ magic][version:u8][length:u32 BE][bincode payload]
- Server + client + WASM accept both envelope and legacy on receive
- Client still sends raw bincode (server handles both)
- Future version → "update required" error instead of crash
- 3 new tests: roundtrip, legacy compat, future version rejection

FC-P4-T3: Periodic Auto-Backup
- Every 5 minutes, encrypts sessions+contacts+sender_keys to ~/.warzone/backups/
- HKDF-derived key from seed, ChaCha20-Poly1305 AEAD
- Atomic writes (temp file + rename), rotates to keep last 3
- /backup command for manual trigger

127 tests passing (was 122)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-03-29 17:03:02 +04:00
parent a368ab24d2
commit 5764719375
13 changed files with 309 additions and 29 deletions

View File

@@ -47,6 +47,7 @@ impl App {
" /info Show your fingerprint",
" /eth Show Ethereum address",
" /seed Show recovery mnemonic (24 words)",
" /backup Create encrypted backup now",
" /peer <fp>, /p Set DM peer by fingerprint",
" /peer @alias Set DM peer by alias",
" /reply, /r Reply to last DM sender",
@@ -189,6 +190,19 @@ impl App {
}
return;
}
if text == "/backup" {
if let Ok(seed) = crate::keystore::load_seed_raw() {
match db.create_backup(&seed) {
Ok(path) => {
self.add_message(ChatLine { sender: "system".into(), text: format!("Backup saved: {}", path.display()), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
}
Err(e) => {
self.add_message(ChatLine { sender: "system".into(), text: format!("Backup failed: {}", e), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
}
}
}
return;
}
if text == "/friend" || text == "/friends" {
// Fetch encrypted friend list from server, decrypt locally
let url = format!("{}/v1/friends", client.base_url);