Skip to content

fix(kerykeion): store-and-forward preserves payload + real stored_at_ms#272

Merged
forkwright merged 1 commit into
mainfrom
fix/189-236-store-forward-payload-ttl
Jul 17, 2026
Merged

fix(kerykeion): store-and-forward preserves payload + real stored_at_ms#272
forkwright merged 1 commit into
mainfrom
fix/189-236-store-forward-payload-ttl

Conversation

@forkwright

Copy link
Copy Markdown
Owner

Summary

Store-and-forward silently discarded outbound traffic in two ways: MeshRouter::send stored an empty packet_bytes payload for offline destinations (Vec::new(), packet argument otherwise unused), and hardcoded stored_at_ms: 0 on every stored message. Together this meant a message routed to an offline node was delivered as an empty, mis-attributed shell once the node came back online, and StoreForward::prune_expired computed every message's expiry against the Unix epoch instead of its real storage time — expiring the entire store against any real wall-clock now_ms.

Closes #189
Closes #236

Changes

  • MeshRouter::send: encode the real packet via prost::Message::encode_to_vec into StoredMessage.packet_bytes, and capture the live portnum from the packet's Decoded payload variant (0 for Encrypted/absent).
  • MeshRouter::send: stored_at_ms is now now_ms() (real epoch milliseconds via jiff::Timestamp::now()), not a hardcoded 0.
  • MeshRouter::node_came_online: rebuilds the original MeshPacket by decoding packet_bytes (MeshPacket::decode) instead of constructing a synthetic packet with from: 0, payload_variant: None, and hardcoded hop_limit/hop_start = 3. A decode failure (never expected — packet_bytes is only ever written by send on this same type) is skipped rather than delivering a garbage packet.

Testing

  • cargo fmt --check passes
  • cargo clippy -p kerykeion --all-targets passes (no new warnings)
  • cargo nextest run -p kerykeion — 207 passed
  • Full kanon gate --stamp — Gate-Passed (763 workspace tests, fmt/check/clippy/nextest/lint all PASS)
  • New test store_forward_round_trips_payload_and_header_on_flush: sends a packet with a real Data payload to an unreachable node, flushes via node_came_online, and asserts the popped message has byte-identical payload plus the original from/hop_limit/hop_start (fails against the old code, which returns payload_variant: None and from: 0).
  • New test store_forward_records_real_wall_clock_and_prunes_after_ttl: sends with ttl_secs: 1 using real wall-clock bounds, asserts the message survives prune_expired within its TTL and is pruned once now_ms passes stored_at_ms + ttl (fails against the old code, which prunes immediately against any real now_ms since stored_at_ms was 0).

MeshRouter::send stored an empty packet_bytes for offline destinations
(Vec::new(), with the packet argument otherwise unused on that branch)
and hardcoded stored_at_ms: 0 on every StoredMessage. Together this
meant a message routed to an offline node was delivered as an empty,
mis-attributed shell once node_came_online flushed it (payload_variant:
None, from: 0, synthetic hop_limit/hop_start = 3), and
StoreForward::prune_expired computed every message's expiry against
the Unix epoch instead of its real storage time, expiring the entire
store against any real wall-clock now_ms.

send now encodes the real packet via prost::Message::encode_to_vec
into packet_bytes, captures the live portnum from the packet's Decoded
payload variant, and records stored_at_ms via jiff::Timestamp::now().
node_came_online decodes packet_bytes back into the original MeshPacket
instead of building a synthetic one, so the flushed message carries the
real payload and original header fields. A decode failure (never
expected, since packet_bytes is only ever written by send on this same
type) is skipped rather than delivering a garbage packet.

Closes #189
Closes #236

Gate-Passed: kanon 0.1.10 +stages:fmt,check,clippy,nextest,lint sha:25bf2452bdc79d46e37b6844c7f3a676f34cedf4
@forkwright
forkwright enabled auto-merge (squash) July 17, 2026 00:05
@forkwright
forkwright merged commit f93ef9a into main Jul 17, 2026
5 checks passed
@forkwright
forkwright deleted the fix/189-236-store-forward-payload-ttl branch July 17, 2026 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

StoredMessage.stored_at_ms is hardcoded to zero: store-forward TTL pruning is broken Store-and-forward discards the outbound packet payload

1 participant