From fce873936ef81e61c4a32e32417801596dd33232 Mon Sep 17 00:00:00 2001 From: DanS Date: Thu, 16 Jul 2026 22:41:43 -0500 Subject: [PATCH] fix(chat): demo identity must not clobber a real one or be a shared constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of the reported "my own messages come back as replies": seedChatDemoData (the Settings "Seed demo chat" debug button) set the chat identity to a FIXED secret ("obsidian-dragon-demo-chat") and, because maybeProvisionChatIdentity no-ops once any identity exists (app_network.cpp:2771), that demo identity stuck and overrode the wallet's real seed-derived one. Clicking it on two different-seed wallets gave BOTH the same constant identity — so they were cryptographically the same person, and a wallet's own outgoing memo (harvested) decrypted back as an incoming message. Two rules now: - Only fabricate a demo identity when there is NO real one (never clobber a provisioned wallet identity). - Derive it from a RANDOM per-run secret, so it can never be a constant shared across installs/wallets. Complements 7351d8a (which removed the self-harvest path); together they close the loopback both at the harvest and at the identity source. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app_network.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/app_network.cpp b/src/app_network.cpp index 54f1567..e74b8a4 100644 --- a/src/app_network.cpp +++ b/src/app_network.cpp @@ -3160,12 +3160,22 @@ void App::seedChatDemoData() // Demo identity so the tab renders the populated UI (hasIdentity()==true). Injected messages go // straight into the in-memory store (no DB attached here → not persisted, gone on restart). - chat::ChatKeyPair keys; - if (chat::deriveChatIdentityFromSecret("obsidian-dragon-demo-chat", keys).status - == chat::ChatIdentityStatus::Ready) { - chat_service_.setIdentity(keys); + // + // Two hard rules, both learned the hard way: + // 1. NEVER clobber a real provisioned identity. Overwriting the wallet's seed-derived identity with + // a demo one made two different-seed wallets share an identity — so their own sends looped back + // and DECRYPTED as incoming. Only fabricate an identity when there is no real one. + // 2. Use a RANDOM secret, never a fixed string. A constant demo secret is the SAME public key on + // every install/wallet, i.e. everyone who ran the demo shared one cryptographic identity. + if (!chat_service_.hasIdentity()) { + chat::ChatKeyPair keys; + const std::string demoSecret = generateChatLocalId("demo-chat:", 24); // random, per-run + if (chat::deriveChatIdentityFromSecret(demoSecret, keys).status + == chat::ChatIdentityStatus::Ready) { + chat_service_.setIdentity(keys); + } + chat::wipeChatKeyPair(keys); } - chat::wipeChatKeyPair(keys); auto& store = chat_service_.store(); const std::string zAlice = "zs1demoalice6xh2n8fchrz23thcgqqd2353v8ev2pr7p7lq4p3elsyrfkuenq";