Messaging Mechanics

Chat Lifecycle & Destruct

Nuke self-destructs, full-mesh group nukes, soft archiving, and pre-reset message recovery.

Overview

Conversations in Wiltkey follow a strict lifecycle. Unlike traditional messaging platforms where accounts and logs are persistent, Wiltkey chats are highly transient. They end either by key exhaustion (wilting), a user-initiated local archive (which preserves chat history but frees large key assets), or a destructive nuke (which wipes cryptographic keys and database logs on both devices).

Nuke vs. Archive Flow

🛑 NUKE (Destructive) Wipes local pad & database logs Sends NUKE_RECIPIENT to peer Locks peer queue in Redis 📦 ARCHIVE (Soft-Nuke) Wipes pad file (saves disk space) Decrypts background messages first Keeps master-encrypted logs readable

How it Works

1. Nuke (Both-Sides Self-Destruct)

When a user nukes a chat locally via nukeContact(receivedFromPeer: false), the client:

  1. Removes the contact row and message windows from active memory.
  2. Deletes the pad file from disk via WiltkeyOtpService.deleteKeystreamFile.
  3. Wipes SQLite records (messages, profiles, lane allocations).
  4. Clears associated caches (e.g. ChatMetaStore, CustomEmojiStore).
  5. Sends a NUKE_RECIPIENT command containing a "VAPORIZE" envelope over the WebSocket.

The Go relay server receives this command, locks the recipient's message queue in Redis (BlockQueue), queues the nuke payload, and pushes a nuke frame immediately if the recipient is online. Upon receiving the nuke frame, the peer client wipes their local logs and responds with an ACK_NUKE message, instructing the server to release the queue block.

2. Group Nuke (Full-Mesh Self-Destruct)

Because there is no central group room, group nukes are fanned out in a full-mesh layout. The initiator sends a group_nuke envelope to every member. When a member client processes this envelope:

  • If they are the host, they re-transmit the group_nuke to all other members (excluding the initiator and themselves) to catch spokes who only know the host, then wipe locally.
  • Spokes wipe local tables and files instantly.

3. Archive (Soft-Nuke)

Archiving drops the large keystream pad file to reclaim storage space while keeping the message history readable. Crucially, before deleting the pad, the client runs a message recovery sweep:

lib/core/state_lifecycle.dart DART
Future<void> _preserveMessagesBeforePadReset(String keyHash) async {
  // Query DB for received-but-never-opened (OTP-only) messages
  final pending = await WiltkeyDatabase.instance.getOtpOnlyMessages(contact.id);
  for (final msg in pending) {
    final plainBytes = await WiltkeyOtpService.xorWithKeystream(keyHash, ...);
    msg.decryptedText = utf8.decode(plainBytes);
    // Write copy encrypted under the master key to DB
    await WiltkeyDatabase.instance.saveMessage(msg, masterKeyHex: masterKeyHex);
  }
}

Once recovery is complete, the pad file is deleted, and the contact is flagged as isArchived = true. The client pushes an archive_signal over the metadata channel. The peer receives this, flags a premature wilt on their end, and posts a system line explaining that the chat is now read-only.

Key Files & Symbols

File Path Symbol Name Description
lib/core/state_lifecycle.dart nukeContact() Deletes local pads, wipes databases, and sends WS self-destruct envelopes.
lib/core/state_lifecycle.dart _preserveMessagesBeforePadReset() Recovers unopened background messages using the active pad before deletion.
lib/core/state_lifecycle.dart archiveChat() Coordinates the soft-nuke archive pipeline and transmits the archive metadata signal.
lib/core/state_lifecycle.dart _fanOutGroupNuke() Fans out group nuke frames to members in a full mesh.

Gotchas & Edge Cases

⚠️ ORPHANED KEYSTREAM RESIDUALS
If the application crashes or is terminated during a nuke, a pad file or contact row might remain orphaned. To clean this up, a master self-destruct (triggerNuke) does a sweep of the entire document folder, reconciling and deleting any file ending in .pad.