The protocol has no duplicate detection. If a message is retransmitted on the I2P network (e.g., due to timeout+retry), it will be stored twice:
Wire format (messaging.rs:19-24):
struct WireMessage {
t: &str, // type ("msg")
id: &str, // UUID (unique)
ct: String, // ciphertext
n: u32, // ratchet counter
}
Problem: While id is unique per message sent, there's no idempotency check on receive:
let entry = MessageEntry {
id: wire.id.clone(),
// ...
};
state.messages.lock().await.push(entry); // No check if id already exists!
Failure scenario:
- Peer sends message with id="abc123", counter=42
- Network delivers it, peer doesn't get ACK immediately
- Peer retransmits with same id, counter=42
- We receive both → push both to state.messages
- User sees the same message twice in the UI
Why this matters:
- I2P doesn't guarantee delivery, apps must handle retransmissions
- User sees duplicate messages
- Can break message count expectations
- TTL expiration tracking gets confused
Proposed solution:
- Keep a set of recently seen message IDs (with TTL)
- Deduplicate on receive: if id already in messages, skip or update
- Or: explicit ACK mechanism (peer sends "msg_ack" with message id)
The protocol has no duplicate detection. If a message is retransmitted on the I2P network (e.g., due to timeout+retry), it will be stored twice:
Wire format (messaging.rs:19-24):
Problem: While
idis unique per message sent, there's no idempotency check on receive:Failure scenario:
Why this matters:
Proposed solution: