When switching between peers, sendDirect() updated _outLinkDestHash to the
new destination before the new link was established, while _outLink still
held the old peer's active link. On the next retry, the hash check passed
(both matched the new peer) but the link object was still connected to the
old peer — delivering the message to the wrong recipient.
Fix: only update _outLinkDestHash in onOutLinkEstablished() when the new
link is actually ready. Use _outLinkPendingHash to track what's being
connected to.
PlatformIO Build / release (push) Has been skipped
PlatformIO Build / build (push) Failing after 7m17s
All fixes from fix/tcp-transport-and-identity-persistence have been
merged into master on ratspeak/microReticulum. No need for the branch
reference anymore.
Outbound (Ratdeck→Python) was broken because Identity::recall() could
never find the recipient. Root cause: OS::time() returns seconds since
boot on ESP32, but persisted known_destinations entries carried timestamps
from the previous session. New announces got timestamp ~31s while persisted
entries had ~5000s, so the LRU cull immediately removed the new entry.
The microReticulum Identity.cpp fix (timestamp normalization on load) is
in .pio/libdeps and must be upstreamed to ratspeak/microReticulum separately.
Changes:
- known_destinations cap 256→512 (PSRAM pool was 1% used, plenty of room)
- Fix link delivery destHash: onLinkEstablished callback was passing link_id
instead of LXMF destination hash, corrupting conversation routing
- Add diagnostic logging: [LXMF-DIAG], [TCP-DIAG], [HEART-DIAG], [DIAG-PROOF]
for tracing link establishment, proof routing, and interface status
PlatformIO Build / build (push) Failing after 12m51s
PlatformIO Build / release (push) Has been skipped
Fix opportunistic LXMF wire format to match Python reference
(strip dest_hash from payload, carried by RNS packet header).
Compute messageId in packFull() matching Python LXMessage.pack().
Prepend dest_hash in onPacketReceived() for correct unpack.
Add opportunistic-first delivery with background link establishment.
Fix boot crash from RNS::Link default constructor in global scope.
Display: switch pushPixelsDMA to blocking pushPixels to prevent SPI bus
contention between the display and SX1262 radio on the shared FSPI bus.
LXMF: add opportunistic-first delivery with background link upgrade.
First message to a peer sends immediately via opportunistic packet. A
link is established in the background; subsequent messages use the link
when active (prepending dest_hash for Python DIRECT format), falling
back to opportunistic otherwise. No user-facing delay.
Opportunistic (non-link) delivery must strip dest_hash from the LXMF
payload — it's carried by the RNS packet header. Python prepends it
back on receive; we now do the same. Previously ratdeck always included
dest_hash when sending and never prepended it when receiving, breaking
interop in both directions.
- packFull: return [src:16][sig:64][msgpack] instead of [dest:16][src:16][sig:64][msgpack]
- onPacketReceived: prepend dest_hash from packet header before unpacking
- sendDirect: use messageId computed by packFull instead of re-hashing wire payload
- Add docs/WIRE-FORMAT.md documenting the exact format for all delivery methods
PlatformIO Build / build (push) Failing after 7m56s
PlatformIO Build / release (push) Has been skipped
messageId was computed as SHA256(dest+src+sig+payload) but Python/Rust
compute SHA256(dest+src+payload) — skipping the 64-byte signature.
This caused message ID mismatches across implementations, breaking
deduplication and propagation node lookups.
PlatformIO Build / release (push) Has been skipped
Apply bin8 fix to the duplicate announce encoding in LvSettingsScreen
(Send Announce action). Replace inline copy with shared function from
main.cpp. Follows up on PR #5.
- Fix TCP port input: allow direct digit entry instead of scroll-only
- Fix flash status always showing "Error" (was checking SD path on flash)
- Flash now shows used/total KB instead of just Mounted/Error
- Audio buffers use PSRAM (ps_malloc) with internal heap fallback
- Bump version to 1.6.1
These standalone devices should not act as transport nodes.
Remove the config toggle, settings UI, and boot-time config peek.
Hardcode transport_enabled(false).
Add post-build script that produces ratdeck-merged.bin with bootloader,
partitions, boot_app0, and firmware at correct offsets. CI now attaches
the merged binary as a standalone release asset alongside the existing
multi-part ZIP.
Standardized checklist for version bumps, tagging, post-release
verification, and hotfix workflow. Moves inline release steps from
BUILDING.md into dedicated RELEASING.md.
- Tighten transport announce rate limit from 5/sec to 2/sec
- Reduce RNS loop frequency from 200Hz to 100Hz
- Throttle LVGL rendering to 30 FPS (was unthrottled)
- Time-box TCP frame loop to 3 frames / 8ms per client
- Add 12ms global TCP budget across all clients
- Replace blocking WiFi announce delay(1500) with deferred non-blocking check
- Rotate persistData() across 3 cycles to spread file I/O
- Reduce LvNodesScreen rebuild frequency (2s → 5s, skip small transient changes)
- Bump version to 1.5.9
Guard AnnounceManager::received_announce() against null Identity
objects — Identity::recall() can fail when known_destinations table
is full and cull removes the entry. Also removes dead-code
needs_transport_headers() override from TCPClientInterface.
- TCPClientInterface overrides needs_transport_headers() so hops==1
packets get HEADER_2 wrapping through the hub
- Track savedCounter in LXMFMessage and use direct filename lookup
for status updates, fixing LoRa uptime-timestamp precision loss
that caused SENT status to revert to QUEUED on reload
- Remove timestamp-based re-sort in loadConversation() which broke
chronological order when mixing LoRa (uptime) and TCP (epoch)
timestamps; counter-based filenames already provide correct order
4-layer defense against 100+ node announce storms from rns.ratspeak.org:
- Transport-level rate limiter (5/sec) via filter_packet callback, before Ed25519 verify
- TCP frame processing time-boxed to 15ms per loop iteration
- Global announce rate limit (3/sec) in AnnounceManager
- UI rebuild throttled to once per 2 seconds
Also fixes message status not persisting to disk on queue drain.
Switch back to maxresdefault.jpg (1280x720) from hqdefault.jpg
(480x360 with black letterbox bars). Add small caption below the
thumbnail so visitors know it's a clickable video.
- Evict highest-hop unsaved nodes first when peer list is full (nearby peers survive)
- Add first-boot SD data cleanup screen before name input
- Conversation summaries cache for faster message list rendering
- Optimize message view refresh with summary-based change detection
- Streamline unread tracking through MessageStore instead of LXMFManager
- Update clone URLs in BUILDING.md and QUICKSTART.md to ratspeak/ratdeck
- Fix update checker API URL in LvSettingsScreen.cpp to ratspeak/ratdeck
- Bump version examples in BUILDING.md from v1.4.2 to v1.5.2
- Add NTP time sync on WiFi STA connect (fixes --:-- timestamps)
- Throttle LXMF send retries to 2s intervals, increase max to 30 (~60s window)
- Restore message status re-save to disk after send completes (QUEUED→SENT/FAILED)
- Update GitHub org URLs to ratspeak
- Fix LXMF wire format: standardize on opportunistic [src:16][sig:64][content]
- Fix LXMF signature: sign(dest||src||packed) per spec, remove message_hash
- Fix MsgPack interop: handle both str and bin types for title/content
- Radio defaults changed to Balanced preset (SF9/BW250k/CR5/TX14)
- Messages screen: sorted by most recent, preview with You:/Them:, green unread dot, timestamps
- Status bar: replace LoRa/BLE/WiFi text with signal bars (green=connected, red=offline)
- Home screen: remove Unread info (shown in Messages tab)
- Contacts screen added (Friends tab)
- Identity manager: multi-slot identity support with per-slot display names
- Message store: fix peer hash truncation, SD directory creation, .bak file leak
- Settings: check for updates, active identity display, info diagnostics