lib/reticulum/src/node/event.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const carrier = @import("../carrier/root.zig");
2 const wire = @import("../wire/root.zig");
3 const timer = @import("timer.zig");
4 const ProofStrategy = @import("../destination/root.zig").registry.ProofStrategy;
5
6 pub const PersistToken = u64;
7
8 pub const CarrierFrame = struct {
9 interface: carrier.Index,
10 now: timer.Seconds,
11 bytes: []const u8,
12 /// Thirty-two bytes of randomness that the caller renews for every frame it gives the node, so
13 /// a link's keys on the receiving side come out of them. A step that needs a key or an
14 /// initialization vector runs HKDF-SHA256 over those bytes, taking the link id as the salt and
15 /// a label that names the use. A responder gets its X25519 private key from this expansion, and
16 /// Reticulum@1.5.0 RNS/Link.py:276 draws one at random. An initiator's round trip packet and a
17 /// responder's LINKCLOSE take their initialization vectors this way, and Reticulum@1.5.0
18 /// RNS/Cryptography/Token.py:89 draws a random one.
19 entropy: [32]u8,
20 };
21
22 pub const TimerExpired = struct {
23 id: timer.TimerId,
24 now: timer.Seconds,
25 /// Thirty-two bytes of randomness that the caller renews for every timer it reports, so a timer
26 /// that closes a link can encrypt. A step driven by a timer derives its keys and initialization
27 /// vectors the same way, through HKDF-SHA256 with the link id as the salt and a label that
28 /// names the use.
29 entropy: [32]u8,
30 };
31
32 pub const ApplicationSend = struct {
33 destination: [16]u8,
34 now: timer.Seconds,
35 plaintext: []const u8,
36 context: wire.Context = .none,
37 hops: u8 = 0,
38 ephemeral_private: [32]u8,
39 iv: [16]u8,
40 create_receipt: bool = true,
41 };
42
43 pub const ApplicationAnnounce = struct {
44 destination: [16]u8,
45 app_data: []const u8,
46 random: [5]u8,
47 fresh_rotating_key: ?[32]u8,
48 now: timer.Seconds,
49 /// The carrier to answer a path request on, which a caller sets on an announce so
50 /// Reticulum@1.5.0 RNS/Transport.py:3379-3382 answers on that carrier alone. Left null, the
51 /// announce goes out on every outgoing carrier.
52 path_response: ?carrier.Index = null,
53 };
54
55 pub const ApplicationProve = struct {
56 destination: [16]u8,
57 packet_hash: [32]u8,
58 interface: carrier.Index,
59 now: timer.Seconds,
60 };
61
62 /// Asks any node that knows a path to the named destination to answer with one, carrying the
63 /// caller's own tag, for a caller that has yet to learn a route to that destination, as
64 /// Reticulum@1.5.0 RNS/Transport.py:3207-3220 requests it. Naming a carrier sends the request on
65 /// that carrier alone, and leaving it null sends it on every outgoing carrier.
66 pub const ApplicationPathRequest = struct {
67 destination: [16]u8,
68 tag: [16]u8,
69 interface: ?carrier.Index,
70 now: timer.Seconds,
71 };
72
73 /// Asks the node to open a link toward a single destination it holds an identity for, learned from
74 /// an announce, for an encrypted session that outlives a single packet. `encryption_private` and
75 /// `signing_private` hold the X25519 and Ed25519 private keys the initiator uses for this session
76 /// alone, and Reticulum@1.5.0 RNS/Link.py:284-285 draws both at random.
77 pub const ApplicationLinkOpen = struct {
78 destination: [16]u8,
79 encryption_private: [32]u8,
80 signing_private: [32]u8,
81 now: timer.Seconds,
82 /// What the node does with data delivered over this link, chosen here for an initiator link
83 /// because it has no registered destination to read it from. Under `.none` the node proves
84 /// nothing, under `.all` it proves every delivered packet at once, and under `.app` it reports
85 /// each delivery with a proof request that a link proof event answers. By contrast, a responder
86 /// reads this from its destination, per Reticulum@1.5.0 RNS/Link.py:958-968.
87 proof_strategy: ProofStrategy = .none,
88 };
89
90 /// Asks the node to put application plaintext across a link that has activated, so the caller can
91 /// send application bytes over the open link. Plaintext over 431 bytes returns
92 /// `error.PacketTooLarge` before the node changes anything, per Reticulum@1.5.0 RNS/Link.py:73.
93 /// With `create_receipt` set, the node tracks the delivery, fails a receipt that stays in the table
94 /// max(rtt * 6, 0.005) seconds after the send when no proof has arrived, per Reticulum@1.5.0
95 /// RNS/Packet.py:419-420 and Reticulum@1.5.0 RNS/Transport.py:1307-1318, and culls the oldest
96 /// receipt and reports it as a `receipt_update` effect with status `culled` and no round trip time
97 /// when a later send that asks for a receipt finds the receipt table full.
98 pub const ApplicationLinkSend = struct {
99 link_id: [16]u8,
100 plaintext: []const u8,
101 iv: [16]u8,
102 now: timer.Seconds,
103 create_receipt: bool = true,
104 };
105
106 /// Asks the node to take down one link it holds open, so a caller can close a link once done with
107 /// it. An encrypted LINKCLOSE goes out, the link leaves the pool, and the caller hears that it
108 /// closed. A link that has yet to activate drops at once with no packet sent. The reason is
109 /// `initiator_closed` on an initiator and `destination_closed` on a responder, per Reticulum@1.5.0
110 /// RNS/Link.py:657-672.
111 pub const ApplicationLinkClose = struct {
112 link_id: [16]u8,
113 iv: [16]u8,
114 now: timer.Seconds,
115 };
116
117 /// Asks the node to answer one packet of link data it has handed up with a proof, so a caller under
118 /// the `.app` strategy can answer a delivery that asked to be proved. The application names the
119 /// packet hash from a delivery whose `proof_requested` was set, and the node sends that hash and a
120 /// signature over it as link plaintext. A packet the node has already forgotten returns
121 /// `error.ProofUnavailable`, per Reticulum@1.5.0 RNS/Link.py:378-389.
122 pub const ApplicationLinkProve = struct {
123 link_id: [16]u8,
124 packet_hash: [32]u8,
125 now: timer.Seconds,
126 };
127
128 pub const StorageComplete = struct {
129 token: PersistToken,
130 now: timer.Seconds,
131 };
132
133 pub const Event = union(enum) {
134 carrier_frame: CarrierFrame,
135 timer_expired: TimerExpired,
136 application_send: ApplicationSend,
137 application_announce: ApplicationAnnounce,
138 application_prove: ApplicationProve,
139 application_path_request: ApplicationPathRequest,
140 application_link_open: ApplicationLinkOpen,
141 application_link_send: ApplicationLinkSend,
142 application_link_close: ApplicationLinkClose,
143 application_link_prove: ApplicationLinkProve,
144 storage_complete: StorageComplete,
145 };