The problem being solved
RNS::AnnounceHandler::received_announce(destination_hash, announced_identity, app_data) is the only way an application learns about announces, and it says nothing about how the announce arrived. Anything that wants to build a neighbour or peer list, show link quality per node, or keep per-interface statistics needs:
- the receiving interface (LoRa vs. TCP vs. a specific peer interface),
- the hop count,
- RSSI/SNR for radio interfaces,
- optionally the transport id and packet hash.
Transport::inbound() has all of this on the Packet at the moment it calls the handler — it has just validated the announce, updated the path table and copied the interface's RSSI/SNR onto the packet — but none of it reaches the callback.
Concrete case: an ESP32-S3 transport node (microReticulum 0.5.0, one LoRa and one TCP interface) that shows a "neighbours" page on its display and web UI. Every entry needs "heard via LoRa at −87 dBm, 1 hop" or "via Wi-Fi client 192.168.x.y". Today the firmware gets the announce twice: once from the handler (no metadata) and once by tapping the raw bytes in its interface implementation, where it re-parses the announce format and re-verifies the Ed25519 signature just to attach the metadata Transport already had.
Why existing functionality is insufficient
- The three-argument callback is the whole public surface;
Transport::_announce_handlers and the dispatch loop are internal.
- Working around it means duplicating announce parsing and signature verification in application code (and keeping it in step with the library), or scraping the interface layer for raw packets, which reintroduces exactly the coupling the handler abstraction is meant to avoid.
Identity::recall() after the fact gives the identity and app data, but nothing about the receiving interface or signal quality — that information exists only on the packet.
Proposed solution
Add an overload that carries the packet, with a default implementation that forwards to the existing method, so current handlers keep working unchanged:
class AnnounceHandler {
public:
// Existing (unchanged)
virtual void received_announce(const Bytes& destination_hash, const Identity& announced_identity, const Bytes& app_data) = 0;
// New: default forwards to the 3-argument form
virtual void received_announce(const Bytes& destination_hash, const Identity& announced_identity, const Bytes& app_data, const Packet& packet) {
received_announce(destination_hash, announced_identity, app_data);
}
};
Transport::inbound() calls the four-argument form. A handler that wants the metadata overrides it and reads packet.receiving_interface(), packet.hops(), packet.rssi(), packet.snr(), packet.transport_id(), packet.packet_hash() — all existing Packet accessors, nothing new to maintain.
Possible alternatives
- A dedicated
AnnounceInfo struct (interface name, hops, rssi, snr, …) passed to a new callback. Cleaner-looking but it is a second copy of data the Packet already carries, and it needs extending every time someone wants one more field.
- A separate "raw announce" callback registered on
Transport. More API surface and it would still need the validated identity/app data to be useful, i.e. it would converge on the overload above.
- Status quo: applications re-parse announces themselves (see above).
Expected impact on existing users
None. Existing AnnounceHandler subclasses implement the three-argument method and are called exactly as before through the default forwarding implementation. The examples (lora_announce, udp_announce) need no change.
Compatibility concerns
- Additive API: one new virtual method with a default body; no protocol, storage or configuration changes.
- The three-argument method stays pure virtual so existing subclasses are unaffected; a new handler that only wants the packet form still has to provide a (possibly empty) three-argument override. Making the three-argument one non-pure would be a follow-up if that turns out to be annoying.
- The
Packet reference is only valid for the duration of the callback (same lifetime rule as elsewhere in Transport::inbound()); handlers must copy what they need.
I have a small, focused PR ready (overload + Transport call site + README "Announce Handlers" section; tested on ESP32-S3 over LoRa and TCP, native test suite 182/182) and will link it to this issue.
The problem being solved
RNS::AnnounceHandler::received_announce(destination_hash, announced_identity, app_data)is the only way an application learns about announces, and it says nothing about how the announce arrived. Anything that wants to build a neighbour or peer list, show link quality per node, or keep per-interface statistics needs:Transport::inbound()has all of this on thePacketat the moment it calls the handler — it has just validated the announce, updated the path table and copied the interface's RSSI/SNR onto the packet — but none of it reaches the callback.Concrete case: an ESP32-S3 transport node (microReticulum 0.5.0, one LoRa and one TCP interface) that shows a "neighbours" page on its display and web UI. Every entry needs "heard via LoRa at −87 dBm, 1 hop" or "via Wi-Fi client 192.168.x.y". Today the firmware gets the announce twice: once from the handler (no metadata) and once by tapping the raw bytes in its interface implementation, where it re-parses the announce format and re-verifies the Ed25519 signature just to attach the metadata
Transportalready had.Why existing functionality is insufficient
Transport::_announce_handlersand the dispatch loop are internal.Identity::recall()after the fact gives the identity and app data, but nothing about the receiving interface or signal quality — that information exists only on the packet.Proposed solution
Add an overload that carries the packet, with a default implementation that forwards to the existing method, so current handlers keep working unchanged:
Transport::inbound()calls the four-argument form. A handler that wants the metadata overrides it and readspacket.receiving_interface(),packet.hops(),packet.rssi(),packet.snr(),packet.transport_id(),packet.packet_hash()— all existingPacketaccessors, nothing new to maintain.Possible alternatives
AnnounceInfostruct (interface name, hops, rssi, snr, …) passed to a new callback. Cleaner-looking but it is a second copy of data thePacketalready carries, and it needs extending every time someone wants one more field.Transport. More API surface and it would still need the validated identity/app data to be useful, i.e. it would converge on the overload above.Expected impact on existing users
None. Existing
AnnounceHandlersubclasses implement the three-argument method and are called exactly as before through the default forwarding implementation. The examples (lora_announce,udp_announce) need no change.Compatibility concerns
Packetreference is only valid for the duration of the callback (same lifetime rule as elsewhere inTransport::inbound()); handlers must copy what they need.I have a small, focused PR ready (overload +
Transportcall site + README "Announce Handlers" section; tested on ESP32-S3 over LoRa and TCP, native test suite 182/182) and will link it to this issue.