* Originate v2 source routes and wire fragmentIdFilter targeted resync Part A — source-route origination policy: - Gate route application (BLESourceRouteOriginationPolicy): only packets we author, directed at a single peer, with TTL headroom, whose recipient is not directly connected. Relays no longer attach routes to (and re-sign) packets they merely forward. - Version-gate paths: MeshTopologyTracker records the highest protocol version observed per peer; BFS routes require every intermediate hop and the recipient to be v2-observed, capped at 4 intermediate hops. - Degrade on failure: BLESourceRouteFailureCache marks a routed send that sees no inbound traffic from the recipient within 10s as failed and floods for 60s before retrying routes. Part B — REQUEST_SYNC fragmentIdFilter (TLV 0x06): - Requester: BLEFragmentAssemblyBuffer reports stalled broadcast reassemblies (no new fragment for 5s, retried at most every 10s); the maintenance pass sends a types=fragment REQUEST_SYNC naming the stalled 8-byte fragment stream IDs to each connected peer. - Responder: GossipSyncManager restricts the fragment diff to exactly the named streams, bypassing the since-cursor while the GCS filter still excludes pieces the requester holds; RSR/TTL-0/rate-limit semantics unchanged and REQUEST_SYNC stays link-local. - Bounds: at most 60 IDs per request (60*17-1 = 1019 bytes <= the 1024-byte decoder cap); oversized 0x06 values are ignored, not fatal. Docs: SOURCE_ROUTING.md gains the iOS origination policy (§8); REQUEST_SYNC_MANAGER.md documents 0x05/0x06 as implemented. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix stall-clock refresh on duplicates and overflow suppression in fragment resync Two fixes to stalledBroadcastFragmentIDs bookkeeping in BLEFragmentAssemblyBuffer: - Duplicate fragments no longer reset the stall clock. Fragment packets bypass the packet deduplicator, so relayed duplicates of an already-held index arriving every few seconds kept lastFragmentAt fresh and suppressed the targeted REQUEST_SYNC indefinitely. Now lastFragmentAt only updates when the index is new (actual progress). - Only the streams that will actually be encoded on the wire are rate-limited. Previously every stalled candidate got lastResyncRequestAt set, but encodeFragmentIdFilter serializes at most RequestSyncPacket.maxFragmentIdFilterCount (60) IDs, so overflow streams were suppressed for retryAfter without ever being requested. Selection now caps at that shared constant, oldest stall first, so overflow stays eligible and rotates fairly on the next pass. Tests: duplicates arriving periodically still trigger the stall report; 70 stalled streams yield the 60 oldest on the first pass and the remaining 10 on the next. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
4.4 KiB
Request Sync Manager & V2 Packet Updates
This document details the implementation of the Request Sync Manager and updates to the V2 packet structure to improve synchronization security and attribution on iOS, mirroring the Android implementation.
Overview
The goal of these changes is to make the request sync functionality "less blind". Previously, sync requests were broadcast, and responses were accepted without strict attribution or timestamp validation (to allow syncing old messages). This opened up potential spoofing vectors and prevented us from enforcing timestamp checks on normal traffic.
The new implementation introduces a RequestSyncManager to track outgoing sync requests and attributes incoming responses (RSR - Request-Sync Response) to specific peers. This allows us to:
- Enforce Timestamp Validation: Normal packets now require timestamps to be within 2 minutes of the local clock.
- Exempt Solicited Sync Responses: Packets marked as RSR are exempt from timestamp validation only if they correspond to a valid, pending sync request sent to that specific peer.
- Prevent Unsolicited Sync Floods: Unsolicited RSR packets are rejected.
Protocol Changes
Binary Protocol Updates
- New Flag:
IS_RSR(0x10) added to the packet header flags. - BitchatPacket: Updated to include
isRSR: Boolfield. - Encoding/Decoding: Updated
BinaryProtocolto handle the new flag.
Request Sync Payload
The REQUEST_SYNC packet payload (TLV encoded) has been updated to include:
sinceTimestamp(Type 0x05): filter-coverage cursor (UInt64 big-endian). The requester's GCS filter only covers packets at or after this timestamp; the responder skips older packets instead of re-sending them every round.fragmentIdFilter(Type 0x06): targeted fragment resync (UTF-8 string). Comma-separated 16-hex-char (8-byte) fragment stream IDs — the ID that prefixes every fragment payload.- Requester: when a broadcast reassembly stalls (no new fragment for 5 s), the fragment assembler reports the stream ID and a
REQUEST_SYNCwithtypes = fragmentand this filter goes to each connected peer (re-requested at most every 10 s per stream). Directed reassemblies are excluded — peers only archive broadcast fragments for sync. - Responder: when the filter is present, the fragment diff is restricted to exactly the named streams and the
sinceTimestampcursor is bypassed for them; the GCS filter still excludes pieces the requester already holds. Responses keep RSR marking, TTL 0, per-peer response rate limiting (8/30 s), andREQUEST_SYNCitself remains link-local (TTL 0, never relayed). - Bounds: at most 60 IDs per request. Each ID encodes as 16 hex chars plus a comma separator, so the largest value is 60 × 17 − 1 = 1019 bytes, within the decoder's 1024-byte acceptance cap; oversized filter values are ignored (the rest of the request still decodes).
- Requester: when a broadcast reassembly stalls (no new fragment for 5 s), the fragment assembler reports the stream ID and a
Architecture
RequestSyncManager
A new component (Sync/RequestSyncManager.swift) responsible for:
- Tracking: Stores
peerID -> timestampmappings for pending sync requests. - Validation:
isValidResponse(from: PeerID, isRSR: Bool)checks if an incoming RSR packet matches a pending request within the 30-second window. - Cleanup: Periodically removes expired requests.
GossipSyncManager Updates
- Unicast Sync: Instead of blind broadcasting, the periodic sync task now iterates over connected peers and sends unicast
REQUEST_SYNCpackets. - Registration: Before sending, requests are registered with
RequestSyncManager. - Response Marking: When responding to a
REQUEST_SYNC, generated packets (Announce/Message) are explicitly marked withisRSR = true(andttl = 0).
BLEService (Security Manager) Updates
- Timestamp Enforcement: Checks
abs(now - packetTimestamp) < 2 minutesfor standard packets. - Conditional Exemption: If
packet.isRSRis true (or packet is a legacy TTL=0 response), it queriesRequestSyncManager.- Valid: If solicited, timestamp check is skipped (allowing historical data sync).
- Invalid: If unsolicited or timed out, the packet is rejected.
Usage
These changes are integrated into BLEService and GossipSyncManager. No external API changes are required for clients, but all peers must be updated to support the new IS_RSR flag and protocol logic to participate in the secure sync process.