diff --git a/docs/html/_sources/examples.rst.txt b/docs/html/_sources/examples.rst.txt index b5e2e69..037ddbe 100644 --- a/docs/html/_sources/examples.rst.txt +++ b/docs/html/_sources/examples.rst.txt @@ -10,56 +10,73 @@ You can use these examples to learn how to write your own programs. Minimal ======= -This example can be found at ``_. The *Minimal* example demonstrates the bare-minimum setup required to connect to a Reticulum network from your program. In about five lines of code, you will have the Reticulum Network Stack initialised, and ready to pass traffic in your program. +.. literalinclude:: ../../Examples/Minimal.py + +This example can also be found at ``_. + .. _example-announce: Announce ======== -This example can be found at ``_. The *Announce* example builds upon the previous example by exploring how to announce a destination on the network, and how to let your program receive notifications about announces from relevant destinations. +.. literalinclude:: ../../Examples/Announce.py + +This example can also be found at ``_. + .. _example-broadcast: Broadcast ========= -This example can be found at ``_. - The *Broadcast* example explores how to transmit plaintext broadcast messages over the network. +.. literalinclude:: ../../Examples/Broadcast.py + +This example can also be found at ``_. + .. _example-echo: Echo ==== -This example can be found at ``_. The *Echo* example demonstrates communication between two destinations using the Packet interface. +.. literalinclude:: ../../Examples/Echo.py + +This example can also be found at ``_. + .. _example-link: Link ==== -This example can be found at ``_. The *Link* example explores establishing an encrypted link to a remote destination, and passing traffic back and forth over the link. +.. literalinclude:: ../../Examples/Link.py + +This example can also be found at ``_. + .. _example-filetransfer: Filetransfer ============ -This example can be found at ``_. The *Filetransfer* example implements a basic file-server program that allow clients to connect and download files. The program uses the Resource -interface to efficiently pass files of any size over a Reticulum :ref:`Link`. \ No newline at end of file +interface to efficiently pass files of any size over a Reticulum :ref:`Link`. + +.. literalinclude:: ../../Examples/Filetransfer.py + +This example can also be found at ``_. \ No newline at end of file diff --git a/docs/html/_sources/understanding.rst.txt b/docs/html/_sources/understanding.rst.txt index c147048..d484527 100644 --- a/docs/html/_sources/understanding.rst.txt +++ b/docs/html/_sources/understanding.rst.txt @@ -10,13 +10,16 @@ develop networked applications using Reticulum. This document is not an exhaustive source of information on Reticulum, at least not yet. Currently, the best place to go for such information is the Python reference implementation of Reticulum, along -with the API reference. +with the code examples and API reference. It is however an essential resource to understanding the +general principles of Reticulum, how to apply them when creating your own networks or software. After reading this document, you should be well-equipped to understand how a Reticulum network operates, what it can achieve, and how you can use it yourself. If you want to help out with the -development, this is also the place to start, since it will also provide a pretty clear overview of the +development, this is also the place to start, since it will provide a pretty clear overview of the sentiments and the philosophy behind Reticulum. +.. _understanding-motivation: + Motivation ========== @@ -26,23 +29,25 @@ belief that it is highly desirable to create a cheap and reliable way to set up communication network that can securely allow exchange of information between people and machines, with no central point of authority, control, censorship or barrier to entry. -Almost all of the various networking stacks in wide use today share a common limitation, namely -that they require large amounts of coordination and trust to work. You can’t just plug in a bunch of -ethernet cables to the same switch, or turn on a number of WiFi radios, and expect such a setup to -provide a reliable platform for communication. - -This need for coordination and trust inevitably leads to an environment of control, where it's very -easy for infrastructure operators or governments to control or alter traffic. +Almost all of the various networking systems in use today share a common limitation, namely that they +require large amounts of coordination and trust to work, and to join the networks you need approval +of gatekeepers in control. This need for coordination and trust inevitably leads to an environment of +central control, where it's very easy for infrastructure operators or governments to control or alter +traffic, and censor or persecute unwanted actors. Reticulum aims to require as little coordination and trust as possible. In fact, the only -“coordination” required is to know how to get connected to a Reticulum network. Since Reticulum -is medium agnostic, this could be whatever is best suited to the situation. In some cases, this might -be 1200 baud packet radio links over VHF frequencies, in other cases it might be a microwave -network using off-the-shelf radios. At the time of release of this document, the recommended setup -is using cheap LoRa radio modules with an open source firmware (see the chapter *Reference System -Setup* ), connected to a small computer like a Raspberry Pi. As an example, the default reference -setup provides a channel capacity of 5.4 Kbps, and a usable direct node-to-node range of around 15 -kilometers (indefinitely extendable by using multiple hops). +“coordination” required is to know the characteristics of physical medium carrying Reticulum traffic. + +Since Reticulum is completely medium agnostic, this could be whatever is best suited to the situation. +In some cases, this might be 1200 baud packet radio links over VHF frequencies, in other cases it might +be a microwave network using off-the-shelf radios. At the time of release of this document, the +recommended setup for development and testing is using LoRa radio modules with an open source firmware +(see the section :ref:`Reference System Setup`), connected to a small +computer like a Raspberry Pi. As an example, the default reference setup provides a channel capacity +of 5.4 Kbps, and a usable direct node-to-node range of around 15 kilometers (indefinitely extendable +by using multiple hops). + +.. _understanding-goals: Goals ===== @@ -52,32 +57,33 @@ guide the design of Reticulum: * **Fully useable as open source software stack** - Reticulum must be implemented, and be able to run using only open source software. This is - critical to ensuring availability, security and transparency of the system. + Reticulum must be implemented with, and be able to run using only open source software. This is + critical to ensuring the availability, security and transparency of the system. * **Hardware layer agnosticism** - Reticulum shall be fully hardware agnostic, and should be useable over a wide range + Reticulum shall be fully hardware agnostic, and shall be useable over a wide range physical networking layers, such as data radios, serial lines, modems, handheld transceivers, wired ethernet, wifi, or anything else that can carry a digital data stream. Hardware made for dedicated Reticulum use shall be as cheap as possible and use off-the-shelf components, so it can be easily replicated. * **Very low bandwidth requirements** - Reticulum should be able to function reliably over links with a data capacity as low as *1,* - *bps*. + Reticulum should be able to function reliably over links with a transmission capacity as low + as *1,000 bps*. * **Encryption by default** Reticulum must use encryption by default where possible and applicable. * **Unlicensed use** Reticulum shall be functional over physical communication mediums that do not require any form of license to use. Reticulum must be designed in a way, so it is usable over ISM radio - frequency bands, and can provide functional long distance links in such conditions. + frequency bands, and can provide functional long distance links in such conditions, for example + by connecting a modem to a PMR or CB radio, or by using LoRa or WiFi modules. * **Supplied software** - Apart from the core networking stack and API, that allows any developer to build + Apart from the core networking stack and API, that allows a developer to build applications with Reticulum, a basic communication suite using Reticulum must be implemented and released at the same time as Reticulum itself. This shall serve both as a functional communication suite, and as an example and learning resource to others wishing to build applications with Reticulum. * **Ease of use** - The reference implementation of Reticulum is written in Python, to make it very easy to use - and understand. Any programmer with only basic experience should be able to use + The reference implementation of Reticulum is written in Python, to make it easy to use + and understand. A programmer with only basic experience should be able to use Reticulum in their own applications. * **Low cost** It shall be as cheap as possible to deploy a communication system based on Reticulum. This @@ -85,30 +91,42 @@ guide the design of Reticulum: own. The cost of setting up a functioning node should be less than $100 even if all parts needs to be purchased. +.. _understanding-basicfunctionality: + Introduction & Basic Functionality ================================== Reticulum is a networking stack suited for high-latency, low-bandwidth links. Reticulum is at it’s -core *message oriented* , but can provide connection oriented sessions. It is suited for both local -point-to-point or point-to-multipoint scenarios where alle nodes are within range of each other, as -well as scenarios where packets need to be transported over multiple hops to reach the recipient. +core a *message oriented* system. It is suited for both local point-to-point or point-to-multipoint +scenarios where alle nodes are within range of each other, as well as scenarios where packets need +to be transported over multiple hops to reach the recipient. Reticulum does away with the idea of addresses and ports known from IP, TCP and UDP. Instead Reticulum uses the singular concept of *destinations*. Any application using Reticulum as it’s networking stack will need to create one or more destinations to receive data, and know the destinations it needs to send data to. -Reticulum encrypts all data by default using public-key cryptography. Any message sent to a -destination is encrypted with that destinations public key. Reticulum also offers symmetric key -encryption for group-oriented communications, as well as unencrypted packets for broadcast -purposes, or situations where you need the communication to be in plain text. The multi-hop -transport, coordination, verification and reliability layers are fully autonomous and based on public -key cryptography. +All destinations in Reticulum are represented internally as 10 bytes, derived from truncating a full +SHA-256 hash of identifying characteristics of the destination. To users, the destination addresses +will be displayed as 10 bytes in hexadecimal representation, as in the following example: ``<80e29bf7cccaf31431b3>``. + +By default Reticulum encrypts all data using public-key cryptography. Any message sent to a +destination is encrypted with that destinations public key. Reticulum can also set up an encrypted +channel to a destination with *Perfect Forward Secrecy* and *Initiator Anonymity* using a elliptic +curve cryptography and ephemeral keys derived from a Diffie Hellman exchange on Curve25519. In +Reticulum terminology, this is called a *Link*. + +Reticulum also offers symmetric key encryption for group-oriented communications, as well as +unencrypted packets for broadcast purposes, or situations where you need the communication to be in +plain text. The multi-hop transport, coordination, verification and reliability layers are fully +autonomous and based on public key cryptography. Reticulum can connect to a variety of interfaces such as radio modems, data radios and serial ports, and offers the possibility to easily tunnel Reticulum traffic over IP links such as the Internet or private IP networks. +.. _understanding-destinations: + Destinations ------------ @@ -127,57 +145,80 @@ destinations. Reticulum uses three different basic destination types, and one sp can by many. * **Plain** A *plain* destination type is unencrypted, and suited for traffic that should be broadcast to a - number of users, or should be readable by anyone. + number of users, or should be readable by anyone. Traffic to a *plain* destination is not encrypted. * **Link** - A *link* is a special destination type, that serves as an abstract channel between two *single* - destinations, directly connected or over multiple hops. The *link* also offers reliability and - more efficient encryption, and as such is useful even when nodes are directly connected. + A *link* is a special destination type, that serves as an abstract channel to a *single* + destination, directly connected or over multiple hops. The *link* also offers reliability and + more efficient encryption, forward secrecy, initiator anonymity, and as such can be useful even + when a node is directly reachable. + +.. _understanding-destinationnaming: Destination Naming ^^^^^^^^^^^^^^^^^^ Destinations are created and named in an easy to understand dotted notation of *aspects* , and represented on the network as a hash of this value. The hash is a SHA-256 truncated to 80 bits. The -top level aspect should always be the a unique identifier for the application using the destination. +top level aspect should always be a unique identifier for the application using the destination. The next levels of aspects can be defined in any way by the creator of the application. For example, -a destination for a messaging application could be made up of the application name and a username, -and look like this: +a destination for a environmental monitoring application could be made up of the application name, a +device type and measurement type, like this: -.. code-block:: +.. code-block:: text - name: simplemessenger.someuser hash: 2a7ddfab5213f916dea + app name : environmentlogger + aspects : remotesensor, temperature + + full name : environmentlogger.remotesensor.temperature + hash : fa7ddfab5213f916dea For the *single* destination, Reticulum will automatically append the associated public key as a destination aspect before hashing. This is done to ensure only the correct destination is reached, since anyone can listen to any destination name. Appending the public key ensures that a given packet is only directed at the destination that holds the corresponding private key to decrypt the -packet. It is important to understand that anyone can use the destination name -*simplemessenger.myusername* , but each person that does so will still have a different destination -hash, because their public keys will differ. In actual use of *single* destination naming, it is advisable -not to use any uniquely identifying features in aspect naming, though. In the simple messenger -example, when using *single* destinations, we would instead use a destination naming scheme such -as *simplemessenger.user* where appending the public key expands the destination into a uniquely -identifying one. +packet. -To recap, the destination types should be used in the following situations: +**Take note!** There is a very important concept to understand here: +* Anyone can use the destination name ``environmentlogger.remotesensor.temperature`` + +* Each destination that does so will still have a unique destination hash, and thus be uniquely + addressable, because their public keys will differ. + +In actual use of *single* destination naming, it is advisable not to use any uniquely identifying +features in aspect naming. Aspect names should be general terms describing what kind of destination +is represented. The uniquely identifying aspect is always acheived by the appending the public key, +which expands the destination into a uniquely identifyable one. + +Any destination on a Reticulum network can be addressed and reached simply by knowning its +destination hash (and public key, but if the public key is not known, it can be requested from the +network simply by knowing the destination hash). The use of app names and aspects makes it easy to +structure Reticulum programs and makes it possible to filter what information and data your program +receives. + +To recap, the different destination types should be used in the following situations: * **Single** - When private communication between two endpoints is needed. Supports routing. + When private communication between two endpoints is needed. Supports multiple hops. * **Group** When private communication between two or more endpoints is needed. More efficient in - data usage than *single* destinations. Supports routing indirectly, but must first be established - through a *single* destination. + data usage than *single* destinations. Supports multiple hops indirectly, but must first be + established through a *single* destination. * **Plain** When plain-text communication is desirable, for example when broadcasting information. To communicate with a *single* destination, you need to know it’s public key. Any method for obtaining the public key is valid, but Reticulum includes a simple mechanism for making other -nodes aware of your destinations public key, called the *announce*. +nodes aware of your destinations public key, called the *announce*. It is also possible to request +an unknown public key from the network, as all participating nodes serve as a distributed ledger +of public keys. -Note that this information could be shared and verified in many other ways, and that it is therefore -not required to use the announce functionality, although it is by far the easiest, and should probably -be used if you are not confident in how to verify public keys and signatures manually. +Note that public key information can be shared and verified in many other ways than using the +built-in methodology, and that it is therefore not required to use the announce/request functionality. +It is by far the easiest though, and should definitely be used if there is not a good reason for +doing it differently. + +.. _understanding-keyannouncements: Public Key Announcements ------------------------ @@ -204,8 +245,11 @@ will be implicit in almost all cases. If a destination name is not entirely impl included in the application specific data part that will allow the receiver to infer the naming. It is important to note that announcements will be forwarded throughout the network according to a -certain pattern. This will be detailed later. Seeing how *single* destinations are always tied to a -private/public key pair leads us to the next topic. +certain pattern. This will be detailed later. + +Seeing how *single* destinations are always tied to a private/public key pair leads us to the next topic. + +.. _understanding-identities: Identities ---------- @@ -227,51 +271,65 @@ application. Destinations created will then be linked to this identity to allow reach the user. In such a case it is of great importance to store the user’s identity securely and privately. +.. _understanding-gettingfurther: + Getting Further --------------- The above functions and principles form the core of Reticulum, and would suffice to create functional networked applications in local clusters, for example over radio links where all interested -nodes can hear each other. But to be truly useful, we need a way to go further. In the next chapter, -two concepts that allow this will be introduced, *paths* and *resources*. +nodes can directly hear each other. But to be truly useful, we need a way to direct traffic over multiple +hops in the network. In the next sections, two concepts that allow this will be introduced, *paths* and +*links*. + +.. _understanding-transport: Reticulum Transport =================== -I have purposefully avoided the term routing until now, and will continue to do so, because the -current methods of routing used in IP based networks are fundamentally incompatible for the link -types that Reticulum was designed to handle. These routing methodologies assume trust at the -physical layer. Since Reticulum is designed to run over open radio spectrum, no such trust exists. -Furthermore, existing routing protocols like BGP or OSPF carry too much overhead to be -practically useable over bandwidth-limited, high-latency links. +The term routing has been purposefully avoided until now. The current methods of routing used in IP-based +networks are fundamentally incompatible with the physical link types that Reticulum was designed to handle. +These routing methodologies assume trust at the physical layer, and often needs a lot more bandwidth than +Reticulum can assume is available. + +Since Reticulum is designed to run over open radio spectrum, no such trust exists, and bandwidth is often +very limited. Existing routing protocols like BGP or OSPF carry too much overhead to be practically +useable over bandwidth-limited, high-latency links. To overcome such challenges, Reticulum’s *Transport* system uses public-key cryptography to implement the concept of *paths* that allow discovery of how to get information to a certain -destination, and *resources* that help alleviate congestion and make reliable communication more -efficient and less bandwidth-hungry. +destination, and *resources* that help make reliable data transfer more efficient. -Threading a Path ----------------- +.. _understanding-paths: + +Reaching the Destination +------------------------ In networks with changing topology and trustless connectivity, nodes need a way to establish -*verified connectivity* with each other. To do this, the following process is employed: +*verified connectivity* with each other. Since the network is assumed to be trustless, Reticulum +must provide a way to guarantee that the peer you are communicating with is actually who you +expect. To do this, the following process is employed: -* First, the node that wishes to establish connectivity will send out a special packet, that +* | First, the node that wishes to establish connectivity will send out a special packet, that traverses the network and locates the desired destination. Along the way, the nodes that forward the packet will take note of this *link request*. -* Second, if the destination accepts the *link request* , it will send back a packet that proves the + +* | Second, if the destination accepts the *link request* , it will send back a packet that proves the authenticity of it’s identity (and the receipt of the link request) to the initiating node. All nodes that initially forwarded the packet will also be able to verify this proof, and thus accept the validity of the *link* throughout the network. -* When the validity of the *link* has been accepted by forwarding nodes, these nodes will + +* | When the validity of the *link* has been accepted by forwarding nodes, these nodes will remember the *link* , and it can subsequently be used by referring to a hash representing it. -* As a part of the *link request* , a Diffie-Hellman key exchange takes place, that sets up an + +* | As a part of the *link request* , a Diffie-Hellman key exchange takes place, that sets up an efficient symmetrically encrypted tunnel between the two nodes, using elliptic curve cryptography. As such, this mode of communication is preferred, even for situations when nodes can directly communicate, when the amount of data to be exchanged numbers in the tens of packets. -* When a *link* has been set up, it automatically provides message receipt functionality, so the + +* | When a *link* has been set up, it automatically provides message receipt functionality, so the sending node can obtain verified confirmation that the information reached the intended recipient. @@ -280,37 +338,44 @@ recap what purposes this serves. We first ensure that the node answering our req one we want to communicate with, and not a malicious actor pretending to be so. At the same time we establish an efficient encrypted channel. The setup of this is relatively cheap in terms of bandwidth, so it can be used just for a short exchange, and then recreated as needed, which will also +rotate encryption keys, but the link can also be kept alive for longer periods of time, if this is +more suitable to the application. The amount of bandwidth used on keeping a link open is practically +negligible. The procedure also inserts the *link id* , a hash calculated from the link request packet, +into the memory of forwarding nodes, which means that the communicating nodes can thereafter reach each +other simply by referring to this *link id*. -rotate encryption keys (keys can also be rotated over an existing path), but the link can also be kept -alive for longer periods of time, if this is more suitable to the application. The amount of bandwidth -used on keeping a link open is practically negligible. The procedure also inserts the *link id* , a hash -calculated from the link request packet, into the memory of forwarding nodes, which means that the -communicating nodes can thereafter reach each other simply by referring to this *link id*. - -**Step 1, pathfinding** +Step 1: Pathfinding +^^^^^^^^^^^^^^^^^^^ The pathfinding method builds on the *announce* functionality discussed earlier. When an announce is sent out by a node, it will be forwarded by any node receiving it, but according to some specific rules: -* If this announce has already been received before, ignore it. -* Record into a table which node the announce was received from, and how many times in +* | If this announce has already been received before, ignore it. + +* | Record into a table which node the announce was received from, and how many times in total it has been retransmitted to get here. -* If the announce has been retransmitted *m+1* times, it will not be forwarded. By default, *m* is + +* | If the announce has been retransmitted *m+1* times, it will not be forwarded. By default, *m* is set to 18. -* The announce will be assigned a delay *d* = *ch* seconds, where *c* is a decay constant, by + +* | The announce will be assigned a delay *d* = c\ :sup:`h` seconds, where *c* is a decay constant, by default 2, and *h* is the amount of times this packet has already been forwarded. -* The packet will be given a priority *p = 1/d*. -* If at least *d* seconds has passed since the announce was received, and no other packets with a + +* | The packet will be given a priority *p = 1/d*. + +* | If at least *d* seconds has passed since the announce was received, and no other packets with a priority higher than *p* are waiting in the queue (see Packet Prioritisation), and the channel is not utilized by other traffic, the announce will be forwarded. -* If no other nodes are heard retransmitting the announce with a greater hop count than when + +* | If no other nodes are heard retransmitting the announce with a greater hop count than when it left this node, transmitting it will be retried *r* times. By default, *r* is set to 2. Retries follow - same rules as above, with the exception that it must wait for at least *d = ch+1 + t* seconds, ie., + same rules as above, with the exception that it must wait for at least *d* = c\ :sup:`h+1` + t seconds, ie., the amount of time it would take the next node to retransmit the packet. By default, *t* is set to 10. -* If a newer announce from the same destination arrives, while an identical one is already in + +* | If a newer announce from the same destination arrives, while an identical one is already in the queue, the newest announce is discarded. If the newest announce contains different application specific data, it will replace the old announce, but will use *d* and *p* of the old announce. @@ -319,17 +384,16 @@ Once an announce has reached a node in the network, any other node in direct con node will be able to reach the destination the announce originated from, simply by sending a packet addressed to that destination. Any node with knowledge of the announce will be able to direct the packet towards the destination by looking up the next node with the shortest amount of hops to the -destination. The specifics of this process is detailed in *Path Calculation*. +destination. According to these rules and default constants, an announce will propagate throughout the network in a predictable way. In an example network utilising the default constants, and with an average link - distance of *Lavg =* 15 kilometers, an announce will be able to propagate outwards to a radius of 180 kilometers in 34 minutes, and a *maximum announce radius* of 270 kilometers in approximately 3 -days. Methods for overcoming the distance limitation of *m * Lavg* will be introduced later in this -chapter. +days. -**Step 2, link establishment** +Step 2: Link Establishment +^^^^^^^^^^^^^^^^^^^^^^^^^^ After seeing how the conditions for finding a path through the network are created, we will now explore how two nodes can establish reliable communications over multiple hops. The *link* in @@ -338,25 +402,30 @@ as an abstract channel, that can be open for any amount of time, and can span an of hops, where information will be exchanged between two nodes. -* When a node in the network wants to establish verified connectivity with another node, it +* | When a node in the network wants to establish verified connectivity with another node, it will create a *link request* packet, and broadcast it. -* The *link request* packet contains the destination hash *Hd* , and an asymmetrically encrypted + +* | The *link request* packet contains the destination hash *Hd* , and an asymmetrically encrypted part containing the following data: The source hash *Hs* , a symmetric key *Lk* , a truncated hash of a random number *Hr* , and a signature *S* of the plaintext values of *Hd* , *Hs* , *Lk* and *Hr*. -* The broadcasted packet will be directed through the network according to the rules laid out + +* | The broadcasted packet will be directed through the network according to the rules laid out previously. -* Any node that forwards the link request will store a *link id* in it’s *link table* , along with the + +* | Any node that forwards the link request will store a *link id* in it’s *link table* , along with the amount of hops the packet had taken when received. The link id is a hash of the entire link request packet. If the path is not *proven* within some set amount of time, the entry will be dropped from the table again. -* When the destination receives the link request packet, it will decide whether to accept the + +* | When the destination receives the link request packet, it will decide whether to accept the request. If it is accepted, it will create a special packet called a *proof*. A *proof* is a simple construct, consisting of a truncated hash of the message that needs to be proven, and a signature (made by the destination’s private key) of this hash. This *proof* effectively verifies that the intended recipient got the packet, and also serves to verify the discovered path through the network. Since the *proof* hash matches the *path id* in the intermediary nodes’ *path tables* , the intermediary nodes can forward the proof all the way back to the source. -* When the source receives the *proof* , it will know unequivocally that a verified path has been + +* | When the source receives the *proof* , it will know unequivocally that a verified path has been established to the destination, and that information can now be exchanged reliably and securely. @@ -372,17 +441,12 @@ of Reticulum, such a retransmission does not need to travel the entire length of If a packet is lost on the 8th hop of a 12 hop path, it can be fetched from the last hop that received it reliably. -Crossing Continents -------------------- +.. _understanding-resources: -When a packet needs to travel farther than local network topology knowledge stretches, a system of -geographical or topological hinting is used to direct the packet towards a network segment with -direct knowledge of the intended destination. This functionality is currently left out of the protocol -for simplicity of testing other parts, but will be activated in a future release. For more information -on when, refer to the roadmap on the website. +Resources +--------- -Resourceful Memory ------------------- +TODO: Write In traditional networks, large amounts of data is rapidly exchanged with very low latency. Links of several thousand kilometers will often only have round-trip latency in the tens of milliseconds, and @@ -415,6 +479,8 @@ certain destination, and as such the network as a whole operates as a distribute For more details on how the caching works and is used, see the reference implementation source code. +.. _understanding-referencesystem: + Reference System Setup ====================== @@ -450,10 +516,9 @@ into the future. The current Reference System Setup is as follows: * **Channel Access Device** A data radio consisting of a LoRa radio module, and a microcontroller with open source firmware, that can connect to host devices via USB. It operates in either the 430, 868 or 900 - MHz frequency bands. More details on the exact parts and how to get/make one can be - found on the website. + MHz frequency bands. More details can be found on the `RNode Page `_. * **Host device** - Any computer device running Linux and Python. A Raspberry Pi with Raspbian is + Any computer device running Linux and Python. A Raspberry Pi with a Debian based OS is recommended. * **Software stack** The current Reference Implementation Release of Reticulum, running on a Debian based @@ -461,11 +526,13 @@ into the future. The current Reference System Setup is as follows: It is very important to note, that the reference channel access device **does not** use the LoRaWAN standard, but uses a custom MAC layer on top of the plain LoRa modulation! As such, you will -need a plain LoRa radio module connected to an MCU with the correct Reticulum firmware. Full -details on how to get or make such a device is available on the website. +need a plain LoRa radio module connected to an MCU with the correct firmware. Full details on how to +get or make such a device is available on the `RNode Page `_. -With the current reference setup, it should be possible to get on a Reticulum network for around 70$ -even if you have none of the hardware already. +With the current reference setup, it should be possible to get on a Reticulum network for around 100$ +even if you have none of the hardware already, and need to purchase everything. + +.. _understanding-protocolspecifics: Protocol Specifics ================== @@ -474,30 +541,114 @@ This chapter will detail protocol specific information that is essential to the Reticulum, but non critical in understanding how the protocol works on a general level. It should be treated more as a reference than as essential reading. + Node Types ---------- Currently Reticulum defines two node types, the *Station* and the *Peer*. A node is a *station* if it fixed -in one place, and if it is intended to be kept online at all times. Otherwise the node is a *peer*. This -distinction is made by the user configuring the node, and is used to determine what nodes on the +in one place, and if it is intended to be kept online most of the time. Otherwise the node is a *peer*. +This distinction is made by the user configuring the node, and is used to determine what nodes on the network will help forward traffic, and what nodes rely on other nodes for connectivity. +If a node is a *Peer* it should be given the configuration directive ``enable_transport = No``. + +If it is a *Station*, it should be given the configuration directive ``enable_transport = Yes``. + + Packet Prioritisation --------------------- -*The packet prioritisation algorithms are subject to rapid change at the moment, and for now, they -are not documented here. See the reference implementation for more info on how this functionality -works.* +Currently, Reticulum is completely priority-agnostic regarding general traffic. All traffic is handled +on a first-come, first-serve basis. Announce re-transmission are handled according to the re-transmission +times and priorities described earlier in this chapter. -Path Calculation ----------------- +It is possible that a prioritisation engine could be added to Reticulum in the future, but in +the light of Reticulums goal of equal access, doing so would need to be the subject of careful +investigation of the consequences first. -*The path calculation algorithms are subject to rapid change at the moment, and for now, they are -not documented here. See the reference implementation for more info on how this functionality -works.* Binary Packet Format -------------------- -*The binary packet format is subject to rapid change at the moment, and for now, it is not -documented here. See the reference implementation for the specific details on this topic.* +.. code-block:: text + + == Reticulum Wire Format ====== + + A Reticulum packet is composed of the following fields: + + [HEADER 2 bytes] [ADDRESSES 10/20 bytes] [CONTEXT 1 byte] [DATA 0-477 bytes] + + * The HEADER field is 2 bytes long. + * Byte 1: [Header Type], [Propagation Type], [Destination Type] and [Packet Type] + * Byte 2: Number of hops + + * The ADDRESSES field contains either 1 or 2 addresses. + * Each address is 10 bytes long. + * The Header Type flag in the HEADER field determines + whether the ADDRESSES field contains 1 or 2 addresses. + * Addresses are Reticulum hashes truncated to 10 bytes. + + * The CONTEXT field is 1 byte. + * It is used by Reticulum to determine packet context. + + * The DATA field is between 0 and 477 bytes. + * It contains the packets data payload. + + Header Types + ----------------- + type 1 00 Two byte header, one 10 byte address field + type 2 01 Two byte header, two 10 byte address fields + type 3 10 Reserved + type 4 11 Reserved + + + Propagation Types + ----------------- + broadcast 00 + transport 01 + reserved 10 + reserved 11 + + + Destination Types + ----------------- + single 00 + group 01 + plain 10 + link 11 + + + Packet Types + ----------------- + data 00 + announce 01 + link request 10 + proof 11 + + + +- Packet Example -+ + + HEADER FIELD ADDRESSES FIELD CONTEXT FIELD DATA FIELD + _______|_______ ________________|________________ ________|______ __|_ + | | | | | | | | + 01010000 00000100 [ADDR1, 10 bytes] [ADDR2, 10 bytes] [CONTEXT, 1 byte] [DATA] + | | | | | + | | | | +-- Hops = 4 + | | | +------- Packet Type = DATA + | | +--------- Destination Type = SINGLE + | +----------- Propagation Type = TRANSPORT + +------------- Header Type = HEADER_2 (two byte header, two address fields) + + + +- Packet Example -+ + + HEADER FIELD ADDRESSES FIELD CONTEXT FIELD DATA FIELD + _______|_______ _______|_______ ________|______ __|_ + | | | | | | | | + 00000000 00000111 [ADDR1, 10 bytes] [CONTEXT, 1 byte] [DATA] + | | | | | + | | | | +-- Hops = 7 + | | | +------- Packet Type = DATA + | | +--------- Destination Type = SINGLE + | +----------- Propagation Type = BROADCAST + +------------- Header Type = HEADER_1 (two byte header, one address field) \ No newline at end of file diff --git a/docs/html/_sources/whatis.rst.txt b/docs/html/_sources/whatis.rst.txt index d5d9578..31ba227 100644 --- a/docs/html/_sources/whatis.rst.txt +++ b/docs/html/_sources/whatis.rst.txt @@ -17,7 +17,7 @@ What does Reticulum Offer? * Asymmetric RSA encryption and signatures as basis for all communication -* Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on the SECP256R1 curve) +* Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on Curve25519) * Reticulum uses the Fernet specification for encryption on links and to group destinations diff --git a/docs/html/examples.html b/docs/html/examples.html index 50feedb..4f26335 100644 --- a/docs/html/examples.html +++ b/docs/html/examples.html @@ -47,43 +47,1372 @@ You can use these examples to learn how to write your own programs.

Minimal

-

This example can be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Minimal.py.

The Minimal example demonstrates the bare-minimum setup required to connect to a Reticulum network from your program. In about five lines of code, you will have the Reticulum Network Stack initialised, and ready to pass traffic in your program.

+
##########################################################
+# This RNS example demonstrates a minimal setup, that    #
+# will start up the Reticulum Network Stack, generate a  #
+# new destination, and let the user send an announce.    #
+##########################################################
+
+import argparse
+import RNS
+
+# Let's define an app name. We'll use this for all
+# destinations we create. Since this basic example
+# is part of a range of example utilities, we'll put
+# them all within the app namespace "example_utilities"
+APP_NAME = "example_utilities"
+
+# This initialisation is executed when the program is started
+def program_setup(configpath):
+    # We must first initialise Reticulum
+    reticulum = RNS.Reticulum(configpath)
+    
+    # Randomly create a new identity for our example
+    identity = RNS.Identity()
+
+    # Using the identity we just created, we create a destination.
+    # Destinations are endpoints in Reticulum, that can be addressed
+    # and communicated with. Destinations can also announce their
+    # existence, which will let the network know they are reachable
+    # and autoomatically create paths to them, from anywhere else
+    # in the network.
+    destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "minimalsample")
+
+    # We configure the destination to automatically prove all
+    # packets adressed to it. By doing this, RNS will automatically
+    # generate a proof for each incoming packet and transmit it
+    # back to the sender of that packet. This will let anyone that
+    # tries to communicate with the destination know whether their
+    # communication was received correctly.
+    destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
+    
+    # Everything's ready!
+    # Let's hand over control to the announce loop
+    announceLoop(destination)
+
+
+def announceLoop(destination):
+    # Let the user know that everything is ready
+    RNS.log("Minimal example "+RNS.prettyhexrep(destination.hash)+" running, hit enter to manually send an announce (Ctrl-C to quit)")
+
+    # We enter a loop that runs until the users exits.
+    # If the user hits enter, we will announce our server
+    # destination on the network, which will let clients
+    # know how to create messages directed towards it.
+    while True:
+        entered = input()
+        destination.announce()
+        RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
+
+
+##########################################################
+#### Program Startup #####################################
+##########################################################
+
+# This part of the program gets run at startup,
+# and parses input from the user, and then starts
+# the desired program mode.
+if __name__ == "__main__":
+    try:
+        parser = argparse.ArgumentParser(description="Bare minimum example to start Reticulum and create a destination")
+        parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
+        args = parser.parse_args()
+
+        if args.config:
+            configarg = args.config
+        else:
+            configarg = None
+
+        program_setup(configarg)
+
+    except KeyboardInterrupt:
+        print("")
+        exit()
+
+
+

This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Minimal.py.

Announce

-

This example can be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Announce.py.

The Announce example builds upon the previous example by exploring how to announce a destination on the network, and how to let your program receive notifications about announces from relevant destinations.

+
##########################################################
+# This RNS example demonstrates setting up announce      #
+# callbacks, which will let an application receive a     #
+# notification when an announce relevant for it arrives  #
+##########################################################
+
+import argparse
+import random
+import RNS
+
+# Let's define an app name. We'll use this for all
+# destinations we create. Since this basic example
+# is part of a range of example utilities, we'll put
+# them all within the app namespace "example_utilities"
+APP_NAME = "example_utilities"
+
+# We initialise two lists of strings to use as app_data
+fruits = ["Peach", "Quince", "Date palm", "Tangerine", "Pomelo", "Carambola", "Grape", "Passion fruit", "Prune", "Cranberry", "Strawberry", "Papaya", "Pomegranate", "Avocado", "Mango"]
+noble_gases = ["Helium", "Neon", "Argon", "Krypton", "Xenon", "Radon", "Oganesson"]
+
+# This initialisation is executed when the program is started
+def program_setup(configpath):
+    # We must first initialise Reticulum
+    reticulum = RNS.Reticulum(configpath)
+    
+    # Randomly create a new identity for our example
+    identity = RNS.Identity()
+
+    # Using the identity we just created, we create two destinations
+    # in the "example_utilities.announcesample" application space.
+    #
+    # Destinations are endpoints in Reticulum, that can be addressed
+    # and communicated with. Destinations can also announce their
+    # existence, which will let the network know they are reachable
+    # and autoomatically create paths to them, from anywhere else
+    # in the network.
+    destination_1 = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "announcesample", "fruits")
+    destination_2 = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "announcesample", "noble_gases")
+
+    # We configure the destinations to automatically prove all
+    # packets adressed to it. By doing this, RNS will automatically
+    # generate a proof for each incoming packet and transmit it
+    # back to the sender of that packet. This will let anyone that
+    # tries to communicate with the destination know whether their
+    # communication was received correctly.
+    destination_1.set_proof_strategy(RNS.Destination.PROVE_ALL)
+    destination_2.set_proof_strategy(RNS.Destination.PROVE_ALL)
+
+    # We create an announce handler and configure it to only ask for
+    # announces from "example_utilities.announcesample.fruits".
+    # Try changing the filter and see what happens.
+    announce_handler = ExampleAnnounceHandler(aspect_filter="example_utilities.announcesample.fruits")
+
+    # We register the announce handler with Reticulum
+    RNS.Transport.register_announce_handler(announce_handler)
+    
+    # Everything's ready!
+    # Let's hand over control to the announce loop
+    announceLoop(destination_1, destination_2)
+
+
+def announceLoop(destination_1, destination_2):
+    # Let the user know that everything is ready
+    RNS.log("Announce example running, hit enter to manually send an announce (Ctrl-C to quit)")
+
+    # We enter a loop that runs until the users exits.
+    # If the user hits enter, we will announce our server
+    # destination on the network, which will let clients
+    # know how to create messages directed towards it.
+    while True:
+        entered = input()
+        
+        # Randomly select a fruit
+        fruit = fruits[random.randint(0,len(fruits)-1)]
+
+        # Send the announce including the app data
+        destination_1.announce(app_data=fruit.encode("utf-8"))
+        RNS.log("Sent announce from "+RNS.prettyhexrep(destination_1.hash)+" ("+destination_1.name+")")
+
+        # Randomly select a noble gas
+        noble_gas = noble_gases[random.randint(0,len(noble_gases)-1)]
+
+        # Send the announce including the app data
+        destination_2.announce(app_data=noble_gas.encode("utf-8"))
+        RNS.log("Sent announce from "+RNS.prettyhexrep(destination_2.hash)+" ("+destination_2.name+")")
+
+# We will need to define an announce handler class that
+# Reticulum can message when an announce arrives.
+class ExampleAnnounceHandler:
+    # The initialisation method takes the optional
+    # aspect_filter argument. If aspect_filter is set to
+    # None, all announces will be passed to the instance.
+    # If only some announces are wanted, it can be set to
+    # an aspect string.
+    def __init__(self, aspect_filter=None):
+        self.aspect_filter = aspect_filter
+
+    # This method will be called by Reticulums Transport
+    # system when an announce arrives that matches the
+    # configured aspect filter. Filters must be specific,
+    # and cannot use wildcards.
+    def received_announce(self, destination_hash, announced_identity, app_data):
+        RNS.log("Received an announce from "+RNS.prettyhexrep(destination_hash))
+        RNS.log("The announce contained the following app data: "+app_data.decode("utf-8"))
+
+##########################################################
+#### Program Startup #####################################
+##########################################################
+
+# This part of the program gets run at startup,
+# and parses input from the user, and then starts
+# the desired program mode.
+if __name__ == "__main__":
+    try:
+        parser = argparse.ArgumentParser(description="Reticulum example that demonstrates announces and announce handlers")
+        parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
+        args = parser.parse_args()
+
+        if args.config:
+            configarg = args.config
+        else:
+            configarg = None
+
+        program_setup(configarg)
+
+    except KeyboardInterrupt:
+        print("")
+        exit()
+
+
+

This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Announce.py.

Broadcast

-

This example can be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Broadcast.py.

The Broadcast example explores how to transmit plaintext broadcast messages over the network.

+
##########################################################
+# This RNS example demonstrates broadcasting unencrypted #
+# information to any listening destinations.             #
+##########################################################
+
+import sys
+import argparse
+import RNS
+
+# Let's define an app name. We'll use this for all
+# destinations we create. Since this basic example
+# is part of a range of example utilities, we'll put
+# them all within the app namespace "example_utilities"
+APP_NAME = "example_utilities"
+
+# This initialisation is executed when the program is started
+def program_setup(configpath, channel=None):
+    # We must first initialise Reticulum
+    reticulum = RNS.Reticulum(configpath)
+    
+    # If the user did not select a "channel" we use
+    # a default one called "public_information".
+    # This "channel" is added to the destination name-
+    # space, so the user can select different broadcast
+    # channels.
+    if channel == None:
+        channel = "public_information"
+
+    # We create a PLAIN destination. This is an uncencrypted endpoint
+    # that anyone can listen to and send information to.
+    broadcast_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, APP_NAME, "broadcast", channel)
+
+    # We specify a callback that will get called every time
+    # the destination receives data.
+    broadcast_destination.packet_callback(packet_callback)
+    
+    # Everything's ready!
+    # Let's hand over control to the main loop
+    broadcastLoop(broadcast_destination)
+
+def packet_callback(data, packet):
+    # Simply print out the received data
+    print("")
+    print("Received data: "+data.decode("utf-8")+"\r\n> ", end="")
+    sys.stdout.flush()
+
+def broadcastLoop(destination):
+    # Let the user know that everything is ready
+    RNS.log("Broadcast example "+RNS.prettyhexrep(destination.hash)+" running, enter text and hit enter to broadcast (Ctrl-C to quit)")
+
+    # We enter a loop that runs until the users exits.
+    # If the user hits enter, we will send the information
+    # that the user entered into the prompt.
+    while True:
+        print("> ", end="")
+        entered = input()
+
+        if entered != "":
+            data    = entered.encode("utf-8")
+            packet  = RNS.Packet(destination, data)
+            packet.send()
+
+
+
+##########################################################
+#### Program Startup #####################################
+##########################################################
+
+# This part of the program gets run at startup,
+# and parses input from the user, and then starts
+# the program.
+if __name__ == "__main__":
+    try:
+        parser = argparse.ArgumentParser(description="Reticulum example that demonstrates sending and receiving unencrypted broadcasts")
+        parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
+        parser.add_argument("--channel", action="store", default=None, help="broadcast channel name", type=str)
+        args = parser.parse_args()
+
+        if args.config:
+            configarg = args.config
+        else:
+            configarg = None
+
+        if args.channel:
+            channelarg = args.channel
+        else:
+            channelarg = None
+
+        program_setup(configarg, channelarg)
+
+    except KeyboardInterrupt:
+        print("")
+        exit()
+
+
+

This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Broadcast.py.

Echo

-

This example can be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Echo.py.

The Echo example demonstrates communication between two destinations using the Packet interface.

+
##########################################################
+# This RNS example demonstrates a simple client/server   #
+# echo utility. A client can send an echo request to the #
+# server, and the server will respond by proving receipt #
+# of the packet.                                         #
+##########################################################
+
+import argparse
+import RNS
+
+# Let's define an app name. We'll use this for all
+# destinations we create. Since this echo example
+# is part of a range of example utilities, we'll put
+# them all within the app namespace "example_utilities"
+APP_NAME = "example_utilities"
+
+
+##########################################################
+#### Server Part #########################################
+##########################################################
+
+# This initialisation is executed when the users chooses
+# to run as a server
+def server(configpath):
+    # We must first initialise Reticulum
+    reticulum = RNS.Reticulum(configpath)
+    
+    # Randomly create a new identity for our echo server
+    server_identity = RNS.Identity()
+
+    # We create a destination that clients can query. We want
+    # to be able to verify echo replies to our clients, so we
+    # create a "single" destination that can receive encrypted
+    # messages. This way the client can send a request and be
+    # certain that no-one else than this destination was able
+    # to read it. 
+    echo_destination = RNS.Destination(server_identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "echo", "request")
+
+    # We configure the destination to automatically prove all
+    # packets adressed to it. By doing this, RNS will automatically
+    # generate a proof for each incoming packet and transmit it
+    # back to the sender of that packet.
+    echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
+    
+    # Tell the destination which function in our program to
+    # run when a packet is received. We do this so we can
+    # print a log message when the server receives a request
+    echo_destination.packet_callback(server_callback)
+
+    # Everything's ready!
+    # Let's Wait for client requests or user input
+    announceLoop(echo_destination)
+
+
+def announceLoop(destination):
+    # Let the user know that everything is ready
+    RNS.log("Echo server "+RNS.prettyhexrep(destination.hash)+" running, hit enter to manually send an announce (Ctrl-C to quit)")
+
+    # We enter a loop that runs until the users exits.
+    # If the user hits enter, we will announce our server
+    # destination on the network, which will let clients
+    # know how to create messages directed towards it.
+    while True:
+        entered = input()
+        destination.announce()
+        RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
+
+
+def server_callback(message, packet):
+    # Tell the user that we received an echo request, and
+    # that we are going to send a reply to the requester.
+    # Sending the proof is handled automatically, since we
+    # set up the destination to prove all incoming packets.
+    RNS.log("Received packet from echo client, proof sent")
+
+
+##########################################################
+#### Client Part #########################################
+##########################################################
+
+# This initialisation is executed when the users chooses
+# to run as a client
+def client(destination_hexhash, configpath, timeout=None):
+    # We need a binary representation of the destination
+    # hash that was entered on the command line
+    try:
+        if len(destination_hexhash) != 20:
+            raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)")
+        destination_hash = bytes.fromhex(destination_hexhash)
+    except:
+        RNS.log("Invalid destination entered. Check your input!\n")
+        exit()
+
+    # We must first initialise Reticulum
+    reticulum = RNS.Reticulum(configpath)
+
+    # We override the loglevel to provide feedback when
+    # an announce is received
+    if RNS.loglevel < RNS.LOG_INFO:
+        RNS.loglevel = RNS.LOG_INFO
+
+    # Tell the user that the client is ready!
+    RNS.log("Echo client ready, hit enter to send echo request to "+destination_hexhash+" (Ctrl-C to quit)")
+
+    # We enter a loop that runs until the user exits.
+    # If the user hits enter, we will try to send an
+    # echo request to the destination specified on the
+    # command line.
+    while True:
+        input()
+        
+        # Let's first check if RNS knows a path to the destination.
+        # If it does, we'll load the server identity and create a packet
+        if RNS.Transport.has_path(destination_hash):
+
+            # To address the server, we need to know it's public
+            # key, so we check if Reticulum knows this destination.
+            # This is done by calling the "recall" method of the
+            # Identity module. If the destination is known, it will
+            # return an Identity instance that can be used in
+            # outgoing destinations.
+            server_identity = RNS.Identity.recall(destination_hash)
+
+            # We got the correct identity instance from the
+            # recall method, so let's create an outgoing
+            # destination. We use the naming convention:
+            # example_utilities.echo.request
+            # This matches the naming we specified in the
+            # server part of the code.
+            request_destination = RNS.Destination(server_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "echo", "request")
+
+            # The destination is ready, so let's create a packet.
+            # We set the destination to the request_destination
+            # that was just created, and the only data we add
+            # is a random hash.
+            echo_request = RNS.Packet(request_destination, RNS.Identity.get_random_hash())
+
+            # Send the packet! If the packet is successfully
+            # sent, it will return a PacketReceipt instance.
+            packet_receipt = echo_request.send()
+
+            # If the user specified a timeout, we set this
+            # timeout on the packet receipt, and configure
+            # a callback function, that will get called if
+            # the packet times out.
+            if timeout != None:
+                packet_receipt.set_timeout(timeout)
+                packet_receipt.timeout_callback(packet_timed_out)
+
+            # We can then set a delivery callback on the receipt.
+            # This will get automatically called when a proof for
+            # this specific packet is received from the destination.
+            packet_receipt.delivery_callback(packet_delivered)
+
+            # Tell the user that the echo request was sent
+            RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
+        else:
+            # If we do not know this destination, tell the
+            # user to wait for an announce to arrive.
+            RNS.log("Destination is not yet known. Requesting path...")
+            RNS.Transport.request_path(destination_hash)
+
+# This function is called when our reply destination
+# receives a proof packet.
+def packet_delivered(receipt):
+    if receipt.status == RNS.PacketReceipt.DELIVERED:
+        rtt = receipt.rtt()
+        if (rtt >= 1):
+            rtt = round(rtt, 3)
+            rttstring = str(rtt)+" seconds"
+        else:
+            rtt = round(rtt*1000, 3)
+            rttstring = str(rtt)+" milliseconds"
+
+        RNS.log("Valid reply received from "+RNS.prettyhexrep(receipt.destination.hash)+", round-trip time is "+rttstring)
+
+# This function is called if a packet times out.
+def packet_timed_out(receipt):
+    if receipt.status == RNS.PacketReceipt.FAILED:
+        RNS.log("Packet "+RNS.prettyhexrep(receipt.hash)+" timed out")
+
+
+##########################################################
+#### Program Startup #####################################
+##########################################################
+
+# This part of the program gets run at startup,
+# and parses input from the user, and then starts
+# the desired program mode.
+if __name__ == "__main__":
+    try:
+        parser = argparse.ArgumentParser(description="Simple echo server and client utility")
+        parser.add_argument("-s", "--server", action="store_true", help="wait for incoming packets from clients")
+        parser.add_argument("-t", "--timeout", action="store", metavar="s", default=None, help="set a reply timeout in seconds", type=float)
+        parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
+        parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the server destination", type=str)
+        args = parser.parse_args()
+
+        if args.server:
+            configarg=None
+            if args.config:
+                configarg = args.config
+            server(configarg)
+        else:
+            if args.config:
+                configarg = args.config
+            else:
+                configarg = None
+
+            if args.timeout:
+                timeoutarg = float(args.timeout)
+            else:
+                timeoutarg = None
+
+            if (args.destination == None):
+                print("")
+                parser.print_help()
+                print("")
+            else:
+                client(args.destination, configarg, timeout=timeoutarg)
+    except KeyboardInterrupt:
+        print("")
+        exit()
+
+
+

This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Echo.py.

Filetransfer

-

This example can be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Filetransfer.py.

The Filetransfer example implements a basic file-server program that allow clients to connect and download files. The program uses the Resource interface to efficiently pass files of any size over a Reticulum Link.

+
##########################################################
+# This RNS example demonstrates a simple filetransfer    #
+# server and client program. The server will serve a     #
+# directory of files, and the clients can list and       #
+# download files from the server.                        #
+#                                                        #
+# Please note that using RNS Resources for large file    #
+# transfers is not recommended, since compression,       #
+# encryption and hashmap sequencing can take a long time #
+# on systems with slow CPUs, which will probably result  #
+# in the client timing out before the resource sender    #
+# can complete preparing the resource.                   #
+#                                                        #
+# If you need to transfer large files, use the Bundle    #
+# class instead, which will automatically slice the data #
+# into chunks suitable for packing as a Resource.        #
+##########################################################
+
+import os
+import sys
+import time
+import threading
+import argparse
+import RNS
+import RNS.vendor.umsgpack as umsgpack
+
+# Let's define an app name. We'll use this for all
+# destinations we create. Since this echo example
+# is part of a range of example utilities, we'll put
+# them all within the app namespace "example_utilities"
+APP_NAME = "example_utilities"
+
+# We'll also define a default timeout, in seconds
+APP_TIMEOUT = 45.0
+
+##########################################################
+#### Server Part #########################################
+##########################################################
+
+serve_path = None
+
+# This initialisation is executed when the users chooses
+# to run as a server
+def server(configpath, path):
+    # We must first initialise Reticulum
+    reticulum = RNS.Reticulum(configpath)
+    
+    # Randomly create a new identity for our file server
+    server_identity = RNS.Identity()
+
+    global serve_path
+    serve_path = path
+
+    # We create a destination that clients can connect to. We
+    # want clients to create links to this destination, so we
+    # need to create a "single" destination type.
+    server_destination = RNS.Destination(server_identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "filetransfer", "server")
+
+    # We configure a function that will get called every time
+    # a new client creates a link to this destination.
+    server_destination.link_established_callback(client_connected)
+
+    # Everything's ready!
+    # Let's Wait for client requests or user input
+    announceLoop(server_destination)
+
+def announceLoop(destination):
+    # Let the user know that everything is ready
+    RNS.log("File server "+RNS.prettyhexrep(destination.hash)+" running")
+    RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
+
+    # We enter a loop that runs until the users exits.
+    # If the user hits enter, we will announce our server
+    # destination on the network, which will let clients
+    # know how to create messages directed towards it.
+    while True:
+        entered = input()
+        destination.announce()
+        RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
+
+# Here's a convenience function for listing all files
+# in our served directory
+def list_files():
+    # We add all entries from the directory that are
+    # actual files, and does not start with "."
+    global serve_path
+    return [file for file in os.listdir(serve_path) if os.path.isfile(os.path.join(serve_path, file)) and file[:1] != "."]
+
+# When a client establishes a link to our server
+# destination, this function will be called with
+# a reference to the link. We then send the client
+# a list of files hosted on the server.
+def client_connected(link):
+    # Check if the served directory still exists
+    if os.path.isdir(serve_path):
+        RNS.log("Client connected, sending file list...")
+
+        link.link_closed_callback(client_disconnected)
+
+        # We pack a list of files for sending in a packet
+        data = umsgpack.packb(list_files())
+
+        # Check the size of the packed data
+        if len(data) <= RNS.Link.MDU:
+            # If it fits in one packet, we will just
+            # send it as a single packet over the link.
+            list_packet = RNS.Packet(link, data)
+            list_receipt = list_packet.send()
+            list_receipt.set_timeout(APP_TIMEOUT)
+            list_receipt.delivery_callback(list_delivered)
+            list_receipt.timeout_callback(list_timeout)
+        else:
+            RNS.log("Too many files in served directory!", RNS.LOG_ERROR)
+            RNS.log("You should implement a function to split the filelist over multiple packets.", RNS.LOG_ERROR)
+            RNS.log("Hint: The client already supports it :)", RNS.LOG_ERROR)
+            
+        # After this, we're just going to keep the link
+        # open until the client requests a file. We'll
+        # configure a function that get's called when
+        # the client sends a packet with a file request.
+        link.packet_callback(client_request)
+    else:
+        RNS.log("Client connected, but served path no longer exists!", RNS.LOG_ERROR)
+        link.teardown()
+
+def client_disconnected(link):
+    RNS.log("Client disconnected")
+
+def client_request(message, packet):
+    global serve_path
+    filename = message.decode("utf-8")
+    if filename in list_files():
+        try:
+            # If we have the requested file, we'll
+            # read it and pack it as a resource
+            RNS.log("Client requested \""+filename+"\"")
+            file = open(os.path.join(serve_path, filename), "rb")
+            file_resource = RNS.Resource(file, packet.link, callback=resource_sending_concluded)
+            file_resource.filename = filename
+        except Exception as e:
+            # If somethign went wrong, we close
+            # the link
+            RNS.log("Error while reading file \""+filename+"\"", RNS.LOG_ERROR)
+            packet.link.teardown()
+            raise e
+    else:
+        # If we don't have it, we close the link
+        RNS.log("Client requested an unknown file")
+        packet.link.teardown()
+
+# This function is called on the server when a
+# resource transfer concludes.
+def resource_sending_concluded(resource):
+    if hasattr(resource, "filename"):
+        name = resource.filename
+    else:
+        name = "resource"
+
+    if resource.status == RNS.Resource.COMPLETE:
+        RNS.log("Done sending \""+name+"\" to client")
+    elif resource.status == RNS.Resource.FAILED:
+        RNS.log("Sending \""+name+"\" to client failed")
+
+def list_delivered(receipt):
+    RNS.log("The file list was received by the client")
+
+def list_timeout(receipt):
+    RNS.log("Sending list to client timed out, closing this link")
+    link = receipt.destination
+    link.teardown()
+
+##########################################################
+#### Client Part #########################################
+##########################################################
+
+# We store a global list of files available on the server
+server_files      = []
+
+# A reference to the server link
+server_link       = None
+
+# And a reference to the current download
+current_download  = None
+current_filename  = None
+
+# Variables to store download statistics
+download_started  = 0
+download_finished = 0
+download_time     = 0
+transfer_size     = 0
+file_size         = 0
+
+
+# This initialisation is executed when the users chooses
+# to run as a client
+def client(destination_hexhash, configpath):
+    # We need a binary representation of the destination
+    # hash that was entered on the command line
+    try:
+        if len(destination_hexhash) != 20:
+            raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)")
+        destination_hash = bytes.fromhex(destination_hexhash)
+    except:
+        RNS.log("Invalid destination entered. Check your input!\n")
+        exit()
+
+    # We must first initialise Reticulum
+    reticulum = RNS.Reticulum(configpath)
+
+
+    # Check if we know a path to the destination
+    if not RNS.Transport.has_path(destination_hash):
+        RNS.log("Destination is not yet known. Requesting path and waiting for announce to arrive...")
+        RNS.Transport.request_path(destination_hash)
+        while not RNS.Transport.has_path(destination_hash):
+            time.sleep(0.1)
+
+    # Recall the server identity
+    server_identity = RNS.Identity.recall(destination_hash)
+
+    # Inform the user that we'll begin connecting
+    RNS.log("Establishing link with server...")
+
+    # When the server identity is known, we set
+    # up a destination
+    server_destination = RNS.Destination(server_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "filetransfer", "server")
+
+    # We also want to automatically prove incoming packets
+    server_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
+
+    # And create a link
+    link = RNS.Link(server_destination)
+
+    # We expect any normal data packets on the link
+    # to contain a list of served files, so we set
+    # a callback accordingly
+    link.packet_callback(filelist_received)
+
+    # We'll also set up functions to inform the
+    # user when the link is established or closed
+    link.link_established_callback(link_established)
+    link.link_closed_callback(link_closed)
+
+    # And set the link to automatically begin
+    # downloading advertised resources
+    link.set_resource_strategy(RNS.Link.ACCEPT_ALL)
+    link.resource_started_callback(download_began)
+    link.resource_concluded_callback(download_concluded)
+
+    menu()
+
+# Requests the specified file from the server
+def download(filename):
+    global server_link, menu_mode, current_filename, transfer_size, download_started
+    current_filename = filename
+    download_started = 0
+    transfer_size    = 0
+
+    # We just create a packet containing the
+    # requested filename, and send it down the
+    # link. We also specify we don't need a
+    # packet receipt.
+    request_packet = RNS.Packet(server_link, filename.encode("utf-8"), create_receipt=False)
+    request_packet.send()
+    
+    print("")
+    print(("Requested \""+filename+"\" from server, waiting for download to begin..."))
+    menu_mode = "download_started"
+
+# This function runs a simple menu for the user
+# to select which files to download, or quit
+menu_mode = None
+def menu():
+    global server_files, server_link
+    # Wait until we have a filelist
+    while len(server_files) == 0:
+        time.sleep(0.1)
+    RNS.log("Ready!")
+    time.sleep(0.5)
+
+    global menu_mode
+    menu_mode = "main"
+    should_quit = False
+    while (not should_quit):
+        print_menu()
+
+        while not menu_mode == "main":
+            # Wait
+            time.sleep(0.25)
+
+        user_input = input()
+        if user_input == "q" or user_input == "quit" or user_input == "exit":
+            should_quit = True
+            print("")
+        else:
+            if user_input in server_files:
+                download(user_input)
+            else:
+                try:
+                    if 0 <= int(user_input) < len(server_files):
+                        download(server_files[int(user_input)])
+                except:
+                    pass
+
+    if should_quit:
+        server_link.teardown()
+
+# Prints out menus or screens for the
+# various states of the client program.
+# It's simple and quite uninteresting.
+# I won't go into detail here. Just
+# strings basically.
+def print_menu():
+    global menu_mode, download_time, download_started, download_finished, transfer_size, file_size
+
+    if menu_mode == "main":
+        clear_screen()
+        print_filelist()
+        print("")
+        print("Select a file to download by entering name or number, or q to quit")
+        print(("> "), end=' ')
+    elif menu_mode == "download_started":
+        download_began = time.time()
+        while menu_mode == "download_started":
+            time.sleep(0.1)
+            if time.time() > download_began+APP_TIMEOUT:
+                print("The download timed out")
+                time.sleep(1)
+                server_link.teardown()
+
+    if menu_mode == "downloading":
+        print("Download started")
+        print("")
+        while menu_mode == "downloading":
+            global current_download
+            percent = round(current_download.progress() * 100.0, 1)
+            print(("\rProgress: "+str(percent)+" %   "), end=' ')
+            sys.stdout.flush()
+            time.sleep(0.1)
+
+    if menu_mode == "save_error":
+        print(("\rProgress: 100.0 %"), end=' ')
+        sys.stdout.flush()
+        print("")
+        print("Could not write downloaded file to disk")
+        current_download.status = RNS.Resource.FAILED
+        menu_mode = "download_concluded"
+
+    if menu_mode == "download_concluded":
+        if current_download.status == RNS.Resource.COMPLETE:
+            print(("\rProgress: 100.0 %"), end=' ')
+            sys.stdout.flush()
+
+            # Print statistics
+            hours, rem = divmod(download_time, 3600)
+            minutes, seconds = divmod(rem, 60)
+            timestring = "{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds)
+            print("")
+            print("")
+            print("--- Statistics -----")
+            print("\tTime taken       : "+timestring)
+            print("\tFile size        : "+size_str(file_size))
+            print("\tData transferred : "+size_str(transfer_size))
+            print("\tEffective rate   : "+size_str(file_size/download_time, suffix='b')+"/s")
+            print("\tTransfer rate    : "+size_str(transfer_size/download_time, suffix='b')+"/s")
+            print("")
+            print("The download completed! Press enter to return to the menu.")
+            print("")
+            input()
+
+        else:
+            print("")
+            print("The download failed! Press enter to return to the menu.")
+            input()
+
+        current_download = None
+        menu_mode = "main"
+        print_menu()
+
+# This function prints out a list of files
+# on the connected server.
+def print_filelist():
+    global server_files
+    print("Files on server:")
+    for index,file in enumerate(server_files):
+        print("\t("+str(index)+")\t"+file)
+
+def filelist_received(filelist_data, packet):
+    global server_files, menu_mode
+    try:
+        # Unpack the list and extend our
+        # local list of available files
+        filelist = umsgpack.unpackb(filelist_data)
+        for file in filelist:
+            if not file in server_files:
+                server_files.append(file)
+
+        # If the menu is already visible,
+        # we'll update it with what was
+        # just received
+        if menu_mode == "main":
+            print_menu()
+    except:
+        RNS.log("Invalid file list data received, closing link")
+        packet.link.teardown()
+
+# This function is called when a link
+# has been established with the server
+def link_established(link):
+    # We store a reference to the link
+    # instance for later use
+    global server_link
+    server_link = link
+
+    # Inform the user that the server is
+    # connected
+    RNS.log("Link established with server")
+    RNS.log("Waiting for filelist...")
+
+    # And set up a small job to check for
+    # a potential timeout in receiving the
+    # file list
+    thread = threading.Thread(target=filelist_timeout_job)
+    thread.setDaemon(True)
+    thread.start()
+
+# This job just sleeps for the specified
+# time, and then checks if the file list
+# was received. If not, the program will
+# exit.
+def filelist_timeout_job():
+    time.sleep(APP_TIMEOUT)
+
+    global server_files
+    if len(server_files) == 0:
+        RNS.log("Timed out waiting for filelist, exiting")
+        os._exit(0)
+
+
+# When a link is closed, we'll inform the
+# user, and exit the program
+def link_closed(link):
+    if link.teardown_reason == RNS.Link.TIMEOUT:
+        RNS.log("The link timed out, exiting now")
+    elif link.teardown_reason == RNS.Link.DESTINATION_CLOSED:
+        RNS.log("The link was closed by the server, exiting now")
+    else:
+        RNS.log("Link closed, exiting now")
+    
+    RNS.Reticulum.exit_handler()
+    time.sleep(1.5)
+    os._exit(0)
+
+# When RNS detects that the download has
+# started, we'll update our menu state
+# so the user can be shown a progress of
+# the download.
+def download_began(resource):
+    global menu_mode, current_download, download_started, transfer_size, file_size
+    current_download = resource
+    
+    if download_started == 0:
+        download_started = time.time()
+    
+    transfer_size += resource.size
+    file_size = resource.total_size
+    
+    menu_mode = "downloading"
+
+# When the download concludes, successfully
+# or not, we'll update our menu state and 
+# inform the user about how it all went.
+def download_concluded(resource):
+    global menu_mode, current_filename, download_started, download_finished, download_time
+    download_finished = time.time()
+    download_time = download_finished - download_started
+
+    saved_filename = current_filename
+
+
+    if resource.status == RNS.Resource.COMPLETE:
+        counter = 0
+        while os.path.isfile(saved_filename):
+            counter += 1
+            saved_filename = current_filename+"."+str(counter)
+
+        try:
+            file = open(saved_filename, "wb")
+            file.write(resource.data.read())
+            file.close()
+            menu_mode = "download_concluded"
+        except:
+            menu_mode = "save_error"
+    else:
+        menu_mode = "download_concluded"
+
+# A convenience function for printing a human-
+# readable file size
+def size_str(num, suffix='B'):
+    units = ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']
+    last_unit = 'Yi'
+
+    if suffix == 'b':
+        num *= 8
+        units = ['','K','M','G','T','P','E','Z']
+        last_unit = 'Y'
+
+    for unit in units:
+        if abs(num) < 1024.0:
+            return "%3.2f %s%s" % (num, unit, suffix)
+        num /= 1024.0
+    return "%.2f %s%s" % (num, last_unit, suffix)
+
+# A convenience function for clearing the screen
+def clear_screen():
+    os.system('cls' if os.name=='nt' else 'clear')
+
+##########################################################
+#### Program Startup #####################################
+##########################################################
+
+# This part of the program runs at startup,
+# and parses input of from the user, and then
+# starts up the desired program mode.
+if __name__ == "__main__":
+    try:
+        parser = argparse.ArgumentParser(description="Simple file transfer server and client utility")
+        parser.add_argument("-s", "--serve", action="store", metavar="dir", help="serve a directory of files to clients")
+        parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str)
+        parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the server destination", type=str)
+        args = parser.parse_args()
+
+        if args.config:
+            configarg = args.config
+        else:
+            configarg = None
+
+        if args.serve:
+            if os.path.isdir(args.serve):
+                server(configarg, args.serve)
+            else:
+                RNS.log("The specified directory does not exist")
+        else:
+            if (args.destination == None):
+                print("")
+                parser.print_help()
+                print("")
+            else:
+                client(args.destination, configarg)
+
+    except KeyboardInterrupt:
+        print("")
+        exit()
+
+
+

This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Filetransfer.py.

diff --git a/docs/html/index.html b/docs/html/index.html index ac89fbe..16328ba 100644 --- a/docs/html/index.html +++ b/docs/html/index.html @@ -87,21 +87,19 @@ using it, or to participate in the development of Reticulum itself.

  • Introduction & Basic Functionality
  • Reticulum Transport
  • Reference System Setup
  • Protocol Specifics
  • diff --git a/docs/html/objects.inv b/docs/html/objects.inv index eec04f6..83b874d 100644 Binary files a/docs/html/objects.inv and b/docs/html/objects.inv differ diff --git a/docs/html/reference.html b/docs/html/reference.html index 94a3849..e027928 100644 --- a/docs/html/reference.html +++ b/docs/html/reference.html @@ -715,7 +715,7 @@ from a the send() method of a
    -CURVE = <cryptography.hazmat.primitives.asymmetric.ec.SECP256R1 object>
    +CURVE = 'Curve25519'

    The curve used for Elliptic Curve DH key exchanges

    diff --git a/docs/html/searchindex.js b/docs/html/searchindex.js index 93445be..8dac0da 100644 --- a/docs/html/searchindex.js +++ b/docs/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["examples","gettingstartedfast","index","reference","understanding","whatis"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["examples.rst","gettingstartedfast.rst","index.rst","reference.rst","understanding.rst","whatis.rst"],objects:{"RNS.Destination":{announce:[3,1,1,""],app_and_aspects_from_name:[3,1,1,""],clear_default_app_data:[3,1,1,""],create_keys:[3,1,1,""],decrypt:[3,1,1,""],encrypt:[3,1,1,""],full_name:[3,1,1,""],get_private_key:[3,1,1,""],hash:[3,1,1,""],hash_from_name_and_identity:[3,1,1,""],link_established_callback:[3,1,1,""],load_private_key:[3,1,1,""],packet_callback:[3,1,1,""],proof_requested_callback:[3,1,1,""],set_default_app_data:[3,1,1,""],set_proof_strategy:[3,1,1,""],sign:[3,1,1,""]},"RNS.Identity":{KEYSIZE:[3,2,1,""],TRUNCATED_HASHLENGTH:[3,2,1,""],decrypt:[3,1,1,""],encrypt:[3,1,1,""],from_file:[3,1,1,""],full_hash:[3,1,1,""],get_private_key:[3,1,1,""],get_public_key:[3,1,1,""],get_random_hash:[3,1,1,""],load_private_key:[3,1,1,""],load_public_key:[3,1,1,""],recall:[3,1,1,""],recall_app_data:[3,1,1,""],sign:[3,1,1,""],to_file:[3,1,1,""],truncated_hash:[3,1,1,""],validate:[3,1,1,""]},"RNS.Link":{CURVE:[3,2,1,""],DEFAULT_TIMEOUT:[3,2,1,""],KEEPALIVE:[3,2,1,""],disable_encryption:[3,1,1,""],inactive_for:[3,1,1,""],no_inbound_for:[3,1,1,""],no_outbound_for:[3,1,1,""],packet_callback:[3,1,1,""],resource_callback:[3,1,1,""],resource_concluded_callback:[3,1,1,""],resource_started_callback:[3,1,1,""],set_resource_strategy:[3,1,1,""],teardown:[3,1,1,""]},"RNS.Packet":{resend:[3,1,1,""],send:[3,1,1,""]},"RNS.PacketReceipt":{delivery_callback:[3,1,1,""],get_status:[3,1,1,""],rtt:[3,1,1,""],set_timeout:[3,1,1,""],timeout_callback:[3,1,1,""]},"RNS.Resource":{MAX_EFFICIENT_SIZE:[3,2,1,""],advertise:[3,1,1,""],cancel:[3,1,1,""],progress:[3,1,1,""]},"RNS.Reticulum":{should_allow_unencrypted:[3,1,1,""],should_use_implicit_proof:[3,1,1,""],transport_enabled:[3,1,1,""]},"RNS.Transport":{deregister_announce_handler:[3,1,1,""],has_path:[3,1,1,""],register_announce_handler:[3,1,1,""],request_path:[3,1,1,""]},RNS:{Destination:[3,0,1,""],Identity:[3,0,1,""],Link:[3,0,1,""],Packet:[3,0,1,""],PacketReceipt:[3,0,1,""],Resource:[3,0,1,""],Reticulum:[3,0,1,""],Transport:[3,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","method","Python method"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:method","2":"py:attribute"},terms:{"0":3,"000":5,"1":[3,4,5],"10":[3,4],"100":4,"1000":4,"1024":3,"12":4,"1200":4,"128":5,"15":[3,4],"16777216":3,"18":4,"180":[3,4],"2":4,"25":5,"256":[3,4],"270":4,"2a7ddfab5213f916dea":4,"3":[4,5],"34":4,"3e12fc71692f8ec47bc5":1,"4":4,"430":4,"5":4,"500":[4,5],"70":4,"80":[3,4],"868":4,"8th":4,"900":4,"abstract":4,"break":2,"byte":[3,4,5],"case":[1,4],"class":[2,5],"default":[3,4],"do":[1,2,4],"float":3,"function":[2,3,5],"import":[1,4],"long":4,"new":[3,4],"public":[2,3],"return":3,"short":4,"static":3,"switch":4,"throw":3,"true":3,"try":2,"while":[2,3,4],A:[0,3,4,5],As:[3,4,5],At:4,Be:3,But:4,By:4,For:[3,4],IN:3,If:[1,2,3,4,5],In:[0,1,4,5],It:[3,4,5],No:5,On:5,One:3,Or:4,That:4,The:[0,1,2,3,4,5],There:2,These:4,To:[2,4],Will:3,With:4,abl:[3,4],about:[0,3,4],abov:[1,4],accept:[3,4],accept_al:3,accept_app:3,accept_non:3,access:[3,4],accord:4,achiev:[3,4],acknowledg:[4,5],act:[3,4],activ:[3,4],actor:4,actual:[1,4],ad:[3,5],addit:[2,4],address:[3,4,5],adress:[3,5],advantag:3,advertis:3,advis:4,ae:5,after:4,again:[1,4],agent:4,agnost:4,agnostic:4,aim:[2,4],algorithm:4,aliv:[3,4],all:[2,3,4,5],allevi:4,alloc:4,allow:[0,3,4,5],almost:4,along:[3,4],alreadi:4,also:[3,4,5],alter:4,although:[4,5],alwai:[3,4],amateur:5,amount:[3,4,5],an:[0,1,2,3,4,5],ani:[0,1,3,4,5],announc:[2,3],announced_ident:3,anonym:4,anoth:[1,3,4],answer:4,anyon:[3,4],anyth:4,apart:4,api:[1,2,4,5],app:3,app_and_aspects_from_nam:3,app_data:3,app_nam:3,append:4,applic:[3,4],approxim:4,ar:[0,2,3,4,5],arbitrari:[3,4],arbritrari:5,area:5,argument:3,around:4,arriv:4,ask:3,aspect:[3,4],aspect_filt:3,assign:4,associ:[1,3,4],assum:4,asymmetr:[3,4,5],attached_interfac:3,attribut:3,audit:2,authent:[4,5],author:4,auto:3,auto_compress:3,autoconfigur:5,autom:4,automat:[3,4,5],autonom:4,avail:[4,5],averag:4,avoid:4,awai:4,awar:4,ax:5,back:[0,4,5],band:4,bandwidth:[4,5],bare:0,barrier:4,base:[2,4,5],basi:[3,5],basic:[0,1,2],baud:4,becaus:4,becom:4,been:[2,3,4,5],befor:[3,4],begin:3,begun:3,behaviour:4,behind:4,being:4,belief:4,below:1,best:[1,2,4],beta:2,between:[0,3,4],bgp:4,binari:2,bit:[3,4,5],blob:[0,4],both:[3,4,5],bp:4,briefli:4,broadcast:[2,3,4],bug:2,build:[0,4,5],built:[1,2,5],bunch:4,c:4,cabl:4,cach:4,cad:4,calcul:2,call:[3,4,5],callabl:3,callback:3,can:[0,1,2,3,4],cancel:3,capac:4,care:[3,5],carri:[3,4],carrier:5,caus:4,cbc:5,cd:1,censorship:4,central:4,certain:4,ch:4,challeng:4,chang:[1,2,4],channel:[4,5],chapter:[1,4],cheap:4,checksum:[3,5],ciphertext:3,clear:[3,4],clear_default_app_data:3,client:[0,1,3],clone:1,close:3,cluster:4,code:[0,4],com:[0,1],command:1,common:4,commun:[0,1,3,4,5],compat:4,complet:[1,5],compon:4,compress:3,comput:[1,4],concept:4,conclud:3,condit:4,confid:4,config:1,configdir:3,configur:[3,4,5],confirm:[4,5],congest:4,connect:[0,3,4,5],consid:[2,4],consist:4,constant:[3,4],constraint:3,construct:4,contact:4,contain:[3,4],context:3,contin:2,continu:4,control:[3,4],coordin:[3,4,5],core:[2,4],correct:4,correspond:4,cost:4,could:[2,3,4],count:4,cover:5,creat:[1,3,4],create_kei:3,create_receipt:3,creation:4,creator:4,critic:4,cross:2,cryptograph:[2,5],cryptographi:[1,2,3,4,5],cull:3,current:[3,4,5],curv:[3,4,5],custom:4,d:4,daemon:3,dai:4,data:[3,4,5],debian:4,debug:3,decai:4,decid:[3,4],decrypt:[3,4],dedic:4,default_timeout:3,defin:[3,4],delai:4,delet:4,deliv:3,deliveri:[3,5],delivery_callback:3,demand:3,demonstr:0,depend:1,deploi:4,deregist:3,deregister_announce_handl:3,describ:[3,4],design:[4,5],desir:4,destin:[0,1,2,5],destination_hash:3,detail:[3,4],determin:[3,4],develop:[2,4],devic:[2,3,4],dh:3,differ:[1,3,4,5],diffi:[4,5],digit:[4,5],direct:[3,4],directli:[3,4,5],disable_encrypt:3,disappear:3,discard:4,discov:4,discoveri:4,discuss:4,disk:3,distanc:4,distinct:4,distribut:[0,3,4],document:4,doe:[2,3,4],don:1,done:4,dot:4,downgrad:3,download:0,driver:5,drop:4,due:4,duplex:[4,5],each:4,earlier:4,eas:4,easi:[4,5],easier:4,easiest:[1,4],easili:[4,5],ec:3,echo:[1,2],effect:4,effici:[0,4,5],either:4,ellipt:[3,4,5],els:4,emploi:4,enabl:3,encapsul:5,encrypt:[0,1,3,4,5],encryptionless:3,end:[3,5],endpoint:[3,4],ensur:4,entir:4,entiti:4,entri:4,environ:4,ephemer:5,equip:4,error:[3,4],essenti:4,establish:[0,3,4],ethernet:[4,5],even:[3,4,5],everi:3,everyon:4,everyth:4,exact:4,exactli:3,exampl:[1,2,3,4,5],exceedingli:4,except:4,exchang:[3,4],execut:3,exhaust:4,exist:[4,5],exit:[1,3],expand:4,expect:4,expens:4,experi:[1,4],experiment:2,explain:3,explicit:3,explicitli:3,explor:[0,2,4],expos:3,extend:4,extern:[2,3],extrem:5,fact:[4,5],fail:[3,4],fals:3,far:4,farther:4,fast:2,featur:[2,4],fernet:5,fetch:4,few:5,file:[0,1,3,5],filetransf:[1,2,3],find:4,firmwar:4,first:[3,4],five:0,fix:4,flag:3,folder:1,follow:[4,5],forcibl:3,foremost:2,form:[3,4],format:2,forth:0,forward:[4,5],found:[0,1,4],free:5,frequenc:4,from:[0,1,3,4,5],from_fil:3,full:[3,4],full_hash:3,full_nam:3,fulli:[4,5],fundament:4,further:[1,2],furthermor:4,futur:[3,4],gener:[4,5],generalis:5,geograph:4,get:[2,3],get_private_kei:3,get_public_kei:3,get_random_hash:3,get_statu:3,gigabyt:[4,5],git:1,github:[0,1],give:4,given:4,global:5,go:[1,4],goal:2,got:4,govern:4,great:4,greater:4,group:[3,4,5],guid:[1,3,4],h:[1,4],ha:[2,3,4,5],had:4,half:[4,5],handheld:4,handl:[3,4,5],handler:3,hardwar:[3,4,5],has_path:3,hash:[1,3,4],hash_from_name_and_ident:3,have:[0,1,3,4],hazard:3,hazmat:3,hd:4,header_typ:3,hear:4,heard:[3,4],hellman:[4,5],help:[2,4],here:4,hide:4,high:[4,5],higher:[4,5],highli:4,hint:4,hmac:5,hoc:5,hold:[3,4],hoop:4,hop:[4,5],host:[4,5],how:[0,3,4,5],hr:4,hs:4,http:[0,1],huge:4,human:3,hungri:4,i:4,id:4,idea:4,ident:2,identif:5,identifi:4,ie:4,ignor:[3,4],immedi:1,implement:[0,2,4,5],implicit:[3,4],improv:4,inactive_for:3,inbound:3,includ:[0,3,4],incom:3,incompat:[3,4],indefinit:4,independ:3,independt:2,index:2,indic:3,indirectli:4,inevit:4,infer:4,info:[3,4],inform:[1,2,3,4],infrastructur:4,ingo:3,initi:4,initialis:[0,3],insert:4,instal:1,instanc:3,instanti:3,instead:4,integr:4,intend:4,intention:4,inter:3,interact:4,interest:4,interfac:[0,2,3,4],intermediari:4,intern:3,internet:[4,5],interv:3,introduc:4,introduct:2,intuit:5,invalid:3,ip:[4,5],ism:4,its:3,itself:[2,3,4],iv:5,jump:4,just:[4,5],kbp:4,keep:[3,4],keepal:3,kei:[2,3,5],kept:[3,4],kernel:5,keyerror:3,keysiz:3,kill:3,kilomet:4,kind:4,know:[3,4],knowledg:4,known:[3,4],lack:4,laid:4,larg:[3,4],last:[3,4],latenc:[4,5],later:4,latest:1,launch:1,lavg:4,layer:[4,5],lead:4,learn:[0,4],least:[4,5],ledger:4,left:4,length:[3,4],less:[4,5],let:[0,1,4],level:4,librari:1,licens:4,like:[1,3,4],limit:4,line:[0,1,4,5],link:[2,4,5],link_established_callback:3,linux:4,list:3,listen:4,littl:4,lk:4,ll:[1,5],ln:1,load:3,load_private_kei:3,load_public_kei:3,local:[3,4,5],locat:4,longer:4,look:[1,4],lookup:4,lora:[4,5],lorawan:4,lost:4,lot:4,low:[4,5],m:4,mac:4,machin:4,made:4,mai:4,maintain:4,make:[1,4],malici:4,manag:3,mani:[4,5],manipul:4,manual:[1,3,4],map:3,markqvist:[0,1],master:[0,3],match:4,max_efficient_s:3,maximum:[3,4],mcu:4,mean:4,mechan:4,medium:[4,5],memori:2,mesh:5,messag:[0,3,4],messeng:4,method:[3,4],methodolog:4,mhz:4,microcontrol:4,microwav:4,might:4,million:4,millisecond:4,mind:2,minim:[2,4],minimum:[0,4],minut:4,mode:[1,4,5],modem:[3,4,5],modul:[4,5],moment:[2,4],more:[3,4,5],most:[1,5],motiv:2,move:1,mtu:[4,5],much:4,multi:[4,5],multilater:4,multipl:4,multipoint:4,must:[3,4],must_compress:3,my:4,myusernam:4,name:3,nano:1,necessari:[1,3,4],necessarili:4,need:[2,4,5],neglig:4,network:[0,1,3,4,5],never:3,newer:4,newest:4,next:[1,4],nicknam:4,no_inbound_for:3,no_outbound_for:3,node:[2,5],nomad:1,non:[3,4],none:[3,4],notat:4,note:4,noth:5,notic:4,notif:[0,3],now:[1,4],number:[0,3,4],object:3,obtain:4,occur:[2,4],off:[4,5],offer:[2,4],often:4,old:4,onc:[4,5],one:[3,4,5],onli:[3,4],onlin:4,open:[3,4,5],openmodem:5,oper:[3,4,5],optic:5,option:1,orient:4,origin:4,original_hash:3,os:5,ospf:4,other:[3,4],otherwis:[3,4],our:4,out:[2,3,4],outbound:3,outgo:[3,4],outlin:[1,4],outward:4,over:[0,3,4,5],overal:4,overcom:4,overhead:4,overview:4,own:[0,1,3,4],owner:3,p:4,packet:[0,2,5],packet_callback:3,packet_receipt:3,packet_typ:3,packetreceipt:3,pad:5,page:2,pair:4,paramet:3,part:4,particip:2,pass:[0,3,4],path:[1,2,3],path_respons:3,pathfind:4,pattern:4,payload:3,peer:[3,4],peer_pub_byt:3,peopl:4,per:[4,5],perfect:5,period:4,person:4,philosophi:4,physic:4,pi:[3,4,5],piec:4,ping:1,pip3:1,pip:1,pkcs7:5,place:4,plain:[3,4],plaintext:[0,3,4],platform:4,pleas:2,plug:4,point:4,port:[4,5],pose:4,possess:4,possibl:[4,5],potenti:4,practic:[2,4,5],predict:4,prefer:4,presenc:3,pretend:4,pretti:4,previou:0,previous:[3,4],primari:4,primit:3,principl:[4,5],prioriti:4,prioritis:2,privaci:[2,4],privat:[3,4,5],probabl:[2,4],procedur:4,process:[1,3,4],product:3,program:[0,2,3,4],programm:4,progress:[3,5],progress_callback:3,proof:[3,4],proof_requested_callback:3,proof_strategi:3,propag:4,protocol:2,prove:4,prove_al:3,prove_app:3,prove_non:3,proven:[3,4],provid:[1,2,3,4,5],prv_byte:3,public_onli:3,purchas:[4,5],purg:3,purpos:[3,4],purposefulli:4,py:[0,1],pyseri:1,python3:1,python:[4,5],queue:4,quickli:4,r:4,radio:[3,4,5],radiu:4,rais:3,random:[3,4],rang:[4,5],rapid:4,rapidli:4,raspberri:[3,4,5],raspbian:4,re:3,reach:4,reachabl:3,read:[1,4],readabl:[3,4],readi:[0,1],readili:5,real:2,reason:3,recal:3,recall_app_data:3,recap:4,receipt:[2,4],receiv:[0,3,4],received_announc:3,recip:1,recipi:4,recommend:4,reconstruct:4,record:4,recreat:4,refer:[1,2],regard:3,regist:3,register_announce_handl:3,rel:[2,4,5],releas:4,relev:0,reli:4,reliabl:[4,5],rememb:4,remot:0,repeat:1,replac:[1,4],replic:4,repositori:1,repres:4,request:[3,4],request_path:3,requir:[0,4,5],research:2,resend:3,resili:5,resourc:[0,2],resource_callback:3,resource_concluded_callback:3,resource_started_callback:3,resource_strategi:3,respond:3,rest:5,reticulum:0,retransmiss:4,retransmit:4,retri:4,reveal:4,review:2,right:1,risk:4,rn:[1,3],rnode:5,roadmap:4,robot:4,rotat:4,round:[3,4],rout:[3,4,5],rsa:[3,5],rtt:3,rule:4,run:[1,3,4,5],runtim:4,s:[1,4,5],said:4,same:[1,3,4],satisfi:4,save:[3,4],scenario:[1,4],scheme:4,search:2,second:[3,4,5],secp256r1:[3,5],secreci:5,section:4,secur:[2,4],see:[3,4],seen:4,segment:4,segment_index:3,self:5,send:[3,4],sender:4,sensibl:1,sensor:4,sent:[3,4],sentiment:4,separ:4,sequenc:[3,5],serial:[4,5],serv:4,server:[0,1],session:4,set:[3,4,5],set_default_app_data:3,set_proof_strategi:3,set_resource_strategi:3,set_timeout:3,setup:[0,2],sever:[3,4],sha256:5,sha:[3,4],shall:4,share:4,shelf:[4,5],shortest:4,should:[2,3,4],should_allow_unencrypt:3,should_use_implicit_proof:3,side:5,sign:[3,4],signatur:[3,4,5],similar:5,simpl:[4,5],simplemesseng:4,simplest:4,simpli:[1,4],simplic:4,sinc:[3,4],singl:[3,4],singular:4,situat:4,size:[0,3],small:[3,4],so:[1,4,5],softwar:[2,4],some:[1,4],someth:[1,4],someus:4,soon:3,sort:4,sourc:[0,1,4,5],space:5,span:4,speak:4,special:4,specif:[2,3,5],specifi:3,spectrum:4,sponsor:2,stabl:[2,4,5],stack:[0,1,4,5],stage:4,stai:4,standard:4,start:[2,3,4],station:4,statu:[3,4],step:[1,4],still:4,stop:5,storag:4,store:4,strategi:3,stream:4,strength:5,stretch:4,string:3,subject:4,subsequ:4,successful:3,suffic:4,suit:[1,4],suitabl:4,suppli:[3,4],support:[2,4],symlink:1,symmetr:[3,4],system:[2,3,5],t:[1,4],tabl:4,take:[1,4,5],taken:4,tcp:[4,5],teardown:3,ten:4,term:4,termin:3,terminolog:4,test:4,text:4,than:4,thei:4,thereaft:4,therefor:[4,5],thi:[0,1,2,3,4],though:4,thourough:2,thousand:4,thread:2,three:4,through:[4,5],throughout:4,throughput:[4,5],thu:4,ti:4,time:[1,3,4],timeout:3,timeout_callback:3,timestamp:4,tnc:[3,5],to_fil:3,todai:4,too:4,tool:5,top:4,topic:4,topolog:4,total:4,touch:2,toward:4,tradeoff:4,tradit:4,traffic:[0,3,4],transceiv:[4,5],transfer:[3,5],transmiss:4,transmit:[0,4],transpar:4,transport:[2,5],transport_en:3,transport_id:3,transport_typ:3,travel:4,travers:4,treat:4,trip:[3,4],trivial:5,truli:4,truncat:[3,4],truncated_hash:3,truncated_hashlength:3,trust:4,trustless:[4,5],tunnel:[4,5],tupl:3,turn:4,two:[0,4],type:[2,3],typeerror:3,udp:[4,5],underli:5,understand:[1,2],unencrypt:[3,4],unequivoc:4,unforg:5,uniqu:[4,5],unknown:3,unless:[1,3,4],unlicens:4,unsupport:3,until:[3,4],up:[4,5],updat:[3,4],upon:[0,4],urandom:5,us:[0,2,3,4],usabl:4,usag:4,usb:[4,5],useabl:4,user:[3,4],userland:5,usernam:4,util:[1,4],utilis:[4,5],valid:[3,4],valu:4,valueerror:3,varieti:[4,5],variou:4,ve:1,veri:[2,3,4,5],verif:[3,4],verifi:4,versa:5,version:3,vhf:4,via:[1,4],vice:5,view:4,wa:[3,4,5],wai:[1,4],wait:4,want:[1,2,4],warrant:2,we:[1,4],websit:4,welcom:[2,4],well:[2,3,4,5],what:[1,2,3,4],whatev:4,when:[1,3,4],whenev:3,where:[2,3,4],whereupon:4,whether:[3,4],which:[1,3,4],who:4,whole:4,wide:[4,5],wifi:[4,5],wire:[2,4,5],wish:4,within:[3,4],work:[4,5],world:2,would:4,write:[0,3],written:4,x:4,year:4,yet:4,you:[0,1,2,3,4,5],your:[0,1,4,5],yourself:[4,5],zero:3},titles:["Examples","Getting Started Fast","Reticulum Network Stack Documentation","API Reference","Understanding Reticulum","What is Reticulum?"],titleterms:{"class":3,"function":4,"public":4,"try":1,announc:[0,4],api:3,base:1,basic:4,binari:4,broadcast:0,calcul:4,can:5,caveat:2,content:2,contin:4,cross:4,current:2,destin:[3,4],develop:1,devic:5,document:2,doe:5,echo:0,emptor:2,exampl:0,fast:1,filetransf:0,format:4,further:4,get:[1,4],goal:4,ident:[3,4],indic:2,interfac:5,introduct:4,kei:4,link:[0,3],memori:4,minim:0,motiv:4,name:4,network:2,node:4,offer:5,packet:[3,4],particip:1,path:4,prioritis:4,program:1,protocol:4,receipt:3,refer:[3,4],resourc:[3,4],reticulum:[1,2,3,4,5],setup:4,specif:4,stack:2,start:1,statu:2,support:5,system:4,tabl:2,thread:4,transport:[3,4],type:[4,5],understand:4,us:[1,5],what:5,where:5}}) \ No newline at end of file +Search.setIndex({docnames:["examples","gettingstartedfast","index","reference","understanding","whatis"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["examples.rst","gettingstartedfast.rst","index.rst","reference.rst","understanding.rst","whatis.rst"],objects:{"RNS.Destination":{announce:[3,1,1,""],app_and_aspects_from_name:[3,1,1,""],clear_default_app_data:[3,1,1,""],create_keys:[3,1,1,""],decrypt:[3,1,1,""],encrypt:[3,1,1,""],full_name:[3,1,1,""],get_private_key:[3,1,1,""],hash:[3,1,1,""],hash_from_name_and_identity:[3,1,1,""],link_established_callback:[3,1,1,""],load_private_key:[3,1,1,""],packet_callback:[3,1,1,""],proof_requested_callback:[3,1,1,""],set_default_app_data:[3,1,1,""],set_proof_strategy:[3,1,1,""],sign:[3,1,1,""]},"RNS.Identity":{KEYSIZE:[3,2,1,""],TRUNCATED_HASHLENGTH:[3,2,1,""],decrypt:[3,1,1,""],encrypt:[3,1,1,""],from_file:[3,1,1,""],full_hash:[3,1,1,""],get_private_key:[3,1,1,""],get_public_key:[3,1,1,""],get_random_hash:[3,1,1,""],load_private_key:[3,1,1,""],load_public_key:[3,1,1,""],recall:[3,1,1,""],recall_app_data:[3,1,1,""],sign:[3,1,1,""],to_file:[3,1,1,""],truncated_hash:[3,1,1,""],validate:[3,1,1,""]},"RNS.Link":{CURVE:[3,2,1,""],DEFAULT_TIMEOUT:[3,2,1,""],KEEPALIVE:[3,2,1,""],disable_encryption:[3,1,1,""],inactive_for:[3,1,1,""],no_inbound_for:[3,1,1,""],no_outbound_for:[3,1,1,""],packet_callback:[3,1,1,""],resource_callback:[3,1,1,""],resource_concluded_callback:[3,1,1,""],resource_started_callback:[3,1,1,""],set_resource_strategy:[3,1,1,""],teardown:[3,1,1,""]},"RNS.Packet":{resend:[3,1,1,""],send:[3,1,1,""]},"RNS.PacketReceipt":{delivery_callback:[3,1,1,""],get_status:[3,1,1,""],rtt:[3,1,1,""],set_timeout:[3,1,1,""],timeout_callback:[3,1,1,""]},"RNS.Resource":{MAX_EFFICIENT_SIZE:[3,2,1,""],advertise:[3,1,1,""],cancel:[3,1,1,""],progress:[3,1,1,""]},"RNS.Reticulum":{should_allow_unencrypted:[3,1,1,""],should_use_implicit_proof:[3,1,1,""],transport_enabled:[3,1,1,""]},"RNS.Transport":{deregister_announce_handler:[3,1,1,""],has_path:[3,1,1,""],register_announce_handler:[3,1,1,""],request_path:[3,1,1,""]},RNS:{Destination:[3,0,1,""],Identity:[3,0,1,""],Link:[3,0,1,""],Packet:[3,0,1,""],PacketReceipt:[3,0,1,""],Resource:[3,0,1,""],Reticulum:[3,0,1,""],Transport:[3,0,1,""]}},objnames:{"0":["py","class","Python class"],"1":["py","method","Python method"],"2":["py","attribute","Python attribute"]},objtypes:{"0":"py:class","1":"py:method","2":"py:attribute"},terms:{"0":[0,3,4],"00":4,"000":[4,5],"00000000":4,"00000100":4,"00000111":4,"01":4,"01010000":4,"05":0,"1":[0,3,5],"10":[0,3,4],"100":[0,4],"1000":[0,4],"1024":[0,3],"11":4,"12":4,"1200":4,"128":5,"15":[3,4],"16777216":3,"18":4,"180":[3,4],"2":0,"20":[0,4],"25":[0,5],"256":[3,4],"270":4,"2f":0,"3":[0,4,5],"34":4,"3600":0,"3e12fc71692f8ec47bc5":1,"4":4,"430":4,"45":0,"477":4,"5":[0,4],"500":[4,5],"60":0,"7":4,"8":0,"80":[3,4],"80e29bf7cccaf31431b3":4,"868":4,"8th":4,"900":4,"abstract":4,"break":2,"byte":[0,3,4,5],"case":[1,4],"class":[0,2,5],"default":[0,3,4],"do":[0,1,2,4],"float":[0,3],"function":[0,2,3,5],"import":[0,1,4],"int":0,"long":[0,4],"new":[0,3,4],"public":[0,2,3],"return":[0,3],"short":4,"static":3,"throw":3,"true":[0,3],"try":[0,2],"while":[0,2,3,4],A:[0,3,4,5],And:0,As:[3,4,5],At:4,Be:3,But:4,By:[0,4],For:[3,4],IN:[0,3],If:[0,1,2,3,4,5],In:[0,1,4,5],It:[0,3,4,5],No:[4,5],On:5,One:3,Or:4,That:4,The:[0,1,2,3,4,5],There:[2,4],These:4,To:[0,2,4],Will:3,With:4,_:4,__:4,______:4,_______:4,________:4,________________:4,__init__:0,__main__:0,__name__:0,_exit:0,ab:0,abl:[0,3,4],about:[0,3,4],abov:[1,4],accept:[3,4],accept_al:[0,3],accept_app:3,accept_non:3,access:[3,4],accord:4,accordingli:0,acheiv:4,achiev:[3,4],acknowledg:[4,5],act:[3,4],action:0,activ:[0,3],actor:4,actual:[0,1,4],ad:[0,3,4,5],add:0,add_argu:0,addit:[2,4],addr1:4,addr2:4,address:[0,3,4,5],adress:[0,3,5],advantag:3,advertis:[0,3],advis:4,ae:5,after:[0,4],again:[1,4],agent:4,agnost:4,agnostic:4,aim:[2,4],aliv:[3,4],all:[0,2,3,4,5],allevi:4,alloc:4,allow:[0,3,4,5],almost:4,along:[3,4],alreadi:[0,4],also:[0,3,4,5],alter:4,altern:0,although:5,alwai:[3,4],amateur:5,amount:[3,4,5],an:[0,1,2,3,4,5],ani:[0,1,3,4,5],announc:[2,3],announce_handl:0,announced_ident:[0,3],announceloop:0,announcesampl:0,anonym:4,anoth:[1,3,4],answer:4,anyon:[0,3,4],anyth:4,anywher:0,apart:4,api:[1,2,4,5],app:[0,3,4],app_and_aspects_from_nam:3,app_data:[0,3],app_nam:[0,3],app_timeout:0,append:[0,4],appli:4,applic:[0,3,4],approv:4,approxim:4,ar:[0,2,3,4,5],arbitrari:[3,4],arbritrari:5,area:5,arg:0,argon:0,argpars:0,argument:[0,3],argumentpars:0,around:4,arriv:[0,4],ask:[0,3],aspect:[0,3,4],aspect_filt:[0,3],assign:4,associ:[1,3,4],assum:4,asymmetr:[4,5],attached_interfac:3,attribut:3,audit:2,authent:[4,5],author:4,auto:3,auto_compress:3,autoconfigur:5,autom:4,automat:[0,3,4,5],autonom:4,autoomat:0,avail:[0,4,5],averag:4,avocado:0,avoid:4,awai:4,awar:4,ax:5,b:0,back:[0,4,5],band:4,bandwidth:[4,5],bare:0,barrier:4,base:[2,4,5],basi:[3,4,5],basic:[0,1,2],baud:4,becaus:4,becom:[0,4],been:[0,2,3,4,5],befor:[0,3,4],begin:[0,3],begun:3,behaviour:4,behind:4,being:4,belief:4,below:1,best:[1,2,4],beta:2,between:[0,3,4],bgp:4,binari:[0,2],bit:[3,4,5],blob:[0,4],both:[3,4,5],bp:4,briefli:4,broadcast:[2,3,4],broadcast_destin:0,broadcastloop:0,bug:2,build:[0,4,5],built:[1,2,4,5],bundl:0,c:[0,4],cach:4,cad:4,calcul:4,call:[0,3,4,5],callabl:3,callback:[0,3],can:[0,1,2,3,4],cancel:3,cannot:0,capac:4,carambola:0,care:[3,4,5],carri:[3,4],carrier:5,caus:4,cb:4,cbc:5,cd:1,censor:4,censorship:4,central:4,certain:[0,4],challeng:4,chang:[0,1,2,4],channel:[0,4,5],channelarg:0,chapter:[1,4],charact:0,characterist:4,cheap:4,check:0,checksum:[3,5],choos:0,chunk:0,ciphertext:3,cl:0,clear:[0,3,4],clear_default_app_data:3,clear_screen:0,client:[0,1,3],client_connect:0,client_disconnect:0,client_loop:0,client_packet_receiv:0,client_request:0,clone:1,close:[0,3],cluster:4,code:[0,4],com:[0,1],come:4,command:[0,1],common:4,commun:[0,1,3,4,5],compat:4,complet:[0,1,4,5],compon:4,compos:4,compress:[0,3],comput:[1,4],concept:4,conclud:[0,3],condit:4,config:[0,1],configarg:0,configdir:3,configpath:0,configur:[0,3,4,5],confirm:[4,5],congest:4,connect:[0,3,4,5],consequ:4,consid:[2,4],consist:4,constant:[3,4],constraint:3,construct:4,contact:4,contain:[0,3,4],context:[3,4],control:[0,3,4],conveni:0,convent:0,coordin:[3,4,5],core:[2,4],correct:[0,4],correctli:0,correspond:4,cost:4,could:[0,2,3,4],count:4,counter:0,cover:5,cpu:0,cranberri:0,creat:[0,1,3,4],create_kei:3,create_receipt:[0,3],creation:4,creator:4,critic:4,cryptograph:[2,5],cryptographi:[1,2,4,5],ctrl:0,cull:3,current:[0,3,4,5],current_download:0,current_filenam:0,curv:[3,4,5],curve25519:[3,4,5],custom:4,d:4,daemon:3,dai:4,data:[0,3,4,5],date:0,debian:4,debug:3,decai:4,decid:[3,4],decod:0,decrypt:[3,4],dedic:4,def:0,default_timeout:3,defin:[0,3,4],definit:4,delai:4,delet:4,deliv:[0,3],deliveri:[0,3,5],delivery_callback:[0,3],demand:3,demonstr:0,depend:1,deploi:4,deregist:3,deregister_announce_handl:3,deriv:4,describ:[3,4],descript:0,design:[4,5],desir:[0,4],destin:[0,1,2,5],destination_1:0,destination_2:0,destination_clos:0,destination_hash:[0,3],destination_hexhash:0,detail:[0,3,4],detect:0,determin:[3,4],develop:[2,4],devic:[2,3,4],dh:3,did:0,differ:[0,1,3,4,5],diffi:[4,5],digit:[4,5],dir:0,direct:[0,3,4],directli:[3,4,5],directori:0,disable_encrypt:3,disappear:3,discard:4,disconnect:0,discov:4,discoveri:4,discuss:4,disk:[0,3],displai:4,distanc:4,distinct:4,distribut:[0,3,4],divmod:0,document:4,doe:[0,2,3,4],don:[0,1],done:[0,4],dot:4,down:0,downgrad:3,download:0,download_began:0,download_conclud:0,download_finish:0,download_start:0,download_tim:0,driver:5,drop:4,due:4,duplex:[4,5],e:0,each:[0,4],earlier:4,eas:4,easi:[4,5],easier:4,easiest:[1,4],easili:[4,5],echo:[1,2],echo_destin:0,echo_request:0,effect:4,effici:[0,4,5],ei:0,either:4,elif:0,ellipt:[3,4,5],els:[0,4],emploi:4,enabl:3,enable_transport:4,encapsul:5,encod:0,encrypt:[0,1,3,4,5],encryptionless:3,end:[0,3,5],endpoint:[0,3,4],engin:4,ensur:4,enter:0,entir:4,entiti:4,entri:[0,4],enumer:0,environ:4,environment:4,environmentlogg:4,ephemer:[4,5],equal:4,equip:4,error:[0,3,4],essenti:4,establish:[0,3],ethernet:[4,5],even:[3,4,5],everi:[0,3],everyon:4,everyth:[0,4],exactli:3,exampl:[1,2,3,4,5],example_util:0,exampleannouncehandl:0,exceedingli:4,except:[0,4],exchang:[3,4],execut:[0,3],exhaust:4,exist:[0,4,5],exit:[0,1,3],exit_handl:0,expand:4,expect:[0,4],expens:4,experi:[1,4],experiment:2,explain:3,explicit:3,explicitli:3,explor:[0,2,4],expos:3,extend:[0,4],extern:[2,3],extrem:5,fa7ddfab5213f916dea:4,fact:[4,5],fail:[0,3,4],fals:[0,3],far:4,fast:2,featur:[2,4],feedback:0,fernet:5,fetch:4,few:5,field:4,file:[0,1,3,5],file_resourc:0,file_s:0,filelist:0,filelist_data:0,filelist_receiv:0,filelist_timeout_job:0,filenam:0,filetransf:[1,2,3],filter:[0,4],find:4,firmwar:4,first:[0,3,4],fit:0,five:0,fix:4,flag:[3,4],flush:0,folder:1,follow:[0,4,5],forcibl:3,foremost:2,form:[3,4],format:[0,2],forth:0,forward:[4,5],found:[0,1,4],free:5,frequenc:4,from:[0,1,3,4,5],from_fil:3,fromhex:0,fruit:0,full:[3,4],full_hash:3,full_nam:3,fulli:[4,5],fundament:4,further:[1,2],futur:[3,4],g:0,ga:0,gatekeep:4,gener:[0,4,5],generalis:5,get:[0,2,3],get_private_kei:3,get_public_kei:3,get_random_hash:[0,3],get_statu:3,gi:0,gigabyt:[4,5],git:1,github:[0,1],give:4,given:4,global:[0,5],go:[0,1,4],goal:2,good:4,got:[0,4],govern:4,grape:0,great:4,greater:4,group:[3,4,5],guarante:4,guid:[1,3,4],h:[1,4],ha:[0,2,3,4,5],had:4,half:[4,5],hand:0,handheld:4,handl:[0,3,4,5],handler:[0,3],happen:0,hardwar:[3,4,5],has_path:[0,3],hasattr:0,hash:[0,1,3,4],hash_from_name_and_ident:3,hashmap:0,have:[0,1,3,4],hazard:3,hd:4,header:4,header_1:4,header_2:4,header_typ:3,hear:4,heard:[3,4],helium:0,hellman:[4,5],help:[0,2,4],here:[0,4],hexadecim:[0,4],hide:4,high:[4,5],higher:[4,5],highli:4,hint:0,hit:0,hmac:5,hoc:5,hold:[3,4],hoop:4,hop:[4,5],host:[0,4,5],hour:0,how:[0,3,4,5],howev:4,hr:4,hs:4,http:[0,1],huge:4,human:[0,3],i:0,id:4,idea:4,ident:[0,2],identif:5,identifi:4,identify:4,ie:4,ignor:[3,4],immedi:1,implement:[0,2,4,5],implicit:[3,4],improv:4,inactive_for:3,inbound:3,includ:[0,3,4],incom:[0,3],incompat:[3,4],indefinit:4,independ:3,independt:2,index:[0,2],indic:3,indirectli:4,inevit:4,infer:4,info:3,inform:[0,1,2,3,4],infrastructur:4,ingo:3,initi:4,initialis:[0,3],input:0,insert:4,instal:1,instanc:[0,3],instanti:3,instead:[0,4],integr:4,intend:4,intention:4,inter:3,interact:[0,4],interest:4,interfac:[0,2,3,4],intermediari:4,intern:[3,4],internet:[4,5],interv:3,introduc:4,introduct:2,intuit:5,invalid:[0,3],investig:4,ip:[4,5],isdir:0,isfil:0,ism:4,its:[3,4],itself:[2,3,4],iv:5,job:0,join:[0,4],jump:4,just:[0,4,5],k:0,kbp:4,keep:[0,3,4],keepal:3,kei:[0,2,3,5],kept:[3,4],kernel:5,keyboardinterrupt:0,keyerror:3,keysiz:3,ki:0,kill:3,kilomet:4,kind:4,know:[0,3,4],knowledg:4,known:[0,3,4],krypton:0,lack:4,laid:4,larg:[0,3,4],last:[0,3,4],last_unit:0,latenc:[4,5],later:[0,4],latest:[0,1],latest_client_link:0,launch:1,lavg:4,layer:[4,5],lead:4,learn:[0,4],least:[4,5],ledger:4,left:4,len:0,length:[0,3,4],less:[4,5],let:[0,1,4],level:4,librari:1,licens:4,light:4,like:[1,3,4],limit:4,line:[0,1,4,5],link:[2,5],link_clos:0,link_closed_callback:0,link_establish:0,link_established_callback:[0,3],linkexampl:0,linux:4,list:[0,3],list_deliv:0,list_fil:0,list_packet:0,list_receipt:0,list_timeout:0,listdir:0,listen:[0,4],littl:4,lk:4,ll:[0,1,5],ln:1,load:[0,3],load_private_kei:3,load_public_kei:3,local:[0,3,4,5],locat:4,log:0,log_error:0,log_info:0,loglevel:0,longer:[0,4],look:[1,4],lookup:4,loop:0,lora:[4,5],lorawan:4,lost:4,lot:4,low:[4,5],m:[0,4],mac:4,machin:4,made:4,mai:4,main:0,maintain:4,make:[1,4],malici:4,manag:3,mango:0,mani:[0,4,5],manipul:4,manual:[0,1,3],map:3,markqvist:[0,1],master:[0,3],match:[0,4],max_efficient_s:3,maximum:[3,4],mcu:4,mdu:0,mean:4,measur:4,mechan:4,medium:[4,5],memori:4,menu:0,menu_mod:0,mesh:5,messag:[0,3,4],messeng:4,metavar:0,method:[0,3,4],methodolog:4,mhz:4,mi:0,microcontrol:4,microwav:4,might:4,million:4,millisecond:[0,4],mind:2,minim:[2,4],minimalsampl:0,minimum:[0,4],minut:[0,4],mode:[0,1,4,5],modem:[3,4,5],modul:[0,4,5],moment:[2,4],monitor:4,more:[3,4,5],most:[1,4,5],motiv:2,move:1,mtu:[4,5],much:4,multi:[4,5],multilater:4,multipl:[0,4],multipoint:4,must:[0,3,4],must_compress:3,my:4,n:0,name:[0,3],namespac:0,nano:1,narg:0,necessari:[1,3,4],necessarili:4,need:[0,2,4,5],neglig:4,neon:0,network:[0,1,3,4,5],never:3,newer:4,newest:4,next:[1,4],nicknam:4,no_inbound_for:3,no_outbound_for:3,nobl:0,noble_ga:0,noble_gas:0,node:[2,5],nomad:1,non:[3,4],none:[0,3,4],normal:0,notat:4,note:[0,4],noth:5,notic:4,notif:[0,3],now:[0,1,4],nt:0,num:0,number:[0,3,4],object:3,obtain:4,occur:[2,4],off:[4,5],offer:[2,4],often:4,oganesson:0,old:4,onc:[4,5],one:[0,3,4,5],onli:[0,3,4],onlin:4,open:[0,3,4,5],openmodem:5,oper:[3,4,5],optic:5,option:[0,1],orient:4,origin:4,original_hash:3,os:[0,4,5],ospf:4,other:[3,4],otherwis:[3,4],our:[0,4],out:[0,2,3,4],outbound:3,outgo:[0,3,4],outlin:[1,4],outward:4,over:[0,3,4,5],overal:4,overcom:4,overhead:4,overrid:0,overview:4,own:[0,1,3,4],owner:3,p:[0,4],pack:0,packb:0,packet:[0,2,5],packet_callback:[0,3],packet_deliv:0,packet_receipt:[0,3],packet_timed_out:0,packet_typ:3,packetreceipt:[0,3],pad:5,page:[2,4],pair:4,palm:0,papaya:0,paramet:3,pars:0,parse_arg:0,parser:0,part:[0,4],particip:[2,4],pass:[0,3,4],passion:0,path:[0,1,3,4],path_respons:3,pattern:4,payload:[3,4],peach:0,peer:[3,4],peer_pub_byt:3,peopl:4,per:[4,5],percent:0,perfect:[4,5],period:4,persecut:4,person:4,philosophi:4,physic:4,pi:[0,3,4,5],piec:4,ping:1,pip3:1,pip:1,pkcs7:5,place:4,plain:[0,3,4],plaintext:[0,3,4],platform:4,pleas:[0,2],pmr:4,point:4,pomegran:0,pomelo:0,port:[4,5],pose:4,possess:4,possibl:[4,5],potenti:[0,4],practic:[2,4,5],predict:4,prefer:4,prepar:0,presenc:3,press:0,pretend:4,pretti:4,prettyhexrep:0,previou:0,previous:[3,4],primari:4,principl:[4,5],print:0,print_filelist:0,print_help:0,print_menu:0,prioriti:4,prioritis:2,privaci:[2,4],privat:[3,4,5],probabl:[0,2,4],procedur:4,process:[1,3,4],product:3,program:[0,2,3,4],program_setup:0,programm:4,progress:[0,3,5],progress_callback:3,prompt:0,proof:[0,3,4],proof_requested_callback:3,proof_strategi:3,propag:4,protocol:2,prove:[0,4],prove_al:[0,3],prove_app:3,prove_non:3,proven:[3,4],provid:[0,1,2,3,4,5],prune:0,prv_byte:3,public_inform:0,public_onli:3,purchas:[4,5],purg:3,purpos:[3,4],purposefulli:4,put:0,py:[0,1],pyseri:1,python3:1,python:[4,5],q:0,queri:0,queue:4,quickli:4,quinc:0,quit:0,r:[0,4],radio:[3,4,5],radiu:4,radon:0,rais:[0,3],randint:0,random:[0,3,4],randomli:0,rang:[0,4,5],rapidli:4,raspberri:[3,4,5],rate:0,rb:0,re:[0,3,4],reach:2,reachabl:[0,3,4],read:[0,1,4],readabl:[0,3,4],readi:[0,1],readili:5,real:2,reason:[3,4],recal:[0,3],recall_app_data:3,recap:4,receipt:[0,2,4],receiv:[0,3,4],received_announc:[0,3],recip:1,recipi:4,recommend:[0,4],reconstruct:4,record:4,recreat:4,refer:[0,1,2],regard:[3,4],regist:[0,3],register_announce_handl:[0,3],rel:[2,4,5],releas:4,relev:0,reli:4,reliabl:[4,5],rem:0,rememb:4,remot:0,remotesensor:4,repeat:1,replac:[1,4],repli:0,replic:4,reply_data:0,reply_text:0,repositori:1,repres:4,represent:[0,4],request:[0,3,4],request_destin:0,request_packet:0,request_path:[0,3],requir:[0,4,5],research:2,resend:3,reserv:4,resili:5,resourc:[0,2],resource_callback:3,resource_concluded_callback:[0,3],resource_sending_conclud:0,resource_started_callback:[0,3],resource_strategi:3,respond:[0,3],rest:5,result:0,reticulum:0,retransmiss:4,retransmit:4,retri:4,reveal:4,review:2,right:1,risk:4,rn:[0,1,3],rnode:[4,5],robot:4,rotat:4,round:[0,3,4],rout:[3,4,5],rprogress:0,rsa:[3,5],rtt:[0,3],rttstring:0,rule:4,run:[0,1,3,4,5],runtim:4,s:[0,1,4,5],said:4,same:[1,3,4],satisfi:4,save:[3,4],save_error:0,saved_filenam:0,scenario:[1,4],screen:0,search:2,second:[0,3,4,5],secreci:[4,5],section:4,secur:[2,4],see:[0,3,4],seen:4,segment_index:3,select:0,self:[0,5],send:[0,3,4],sender:[0,4],sensibl:1,sensor:4,sent:[0,3,4],sentiment:4,separ:4,sequenc:[0,3,5],serial:[4,5],serv:[0,4],serve_path:0,server:[0,1],server_callback:0,server_destin:0,server_fil:0,server_ident:0,server_link:0,server_loop:0,server_packet_receiv:0,set:[0,3,4,5],set_default_app_data:3,set_proof_strategi:[0,3],set_resource_strategi:[0,3],set_timeout:[0,3],setdaemon:0,setup:[0,2],sever:[3,4],sha256:5,sha:[3,4],shall:4,share:4,shelf:[4,5],shortest:4,should:[0,2,3,4],should_allow_unencrypt:3,should_quit:0,should_use_implicit_proof:3,shown:0,side:5,sign:[3,4],signatur:[3,4,5],similar:5,simpl:[0,4,5],simplest:4,simpli:[0,1,4],sinc:[0,3,4],singl:[0,3,4],singular:4,situat:4,size:[0,3],size_str:0,sleep:0,slice:0,slow:0,small:[0,3,4],so:[0,1,4,5],softwar:[2,4],some:[0,1,4],someth:[1,4],somethign:0,soon:3,sort:4,sourc:[0,1,4,5],space:[0,5],span:4,speak:4,special:4,specif:[0,2,3,5],specifi:[0,3],spectrum:4,split:0,sponsor:2,stabl:[2,4,5],stack:[0,1,4,5],stage:4,stai:4,standard:4,start:[0,2,3,4],startup:0,state:0,station:4,statist:0,statu:[0,3,4],stdout:0,step:1,still:[0,4],stop:5,storag:4,store:[0,4],store_tru:0,str:0,strategi:3,strawberri:0,stream:4,strength:5,string:[0,3],structur:4,subject:4,subsequ:4,successful:3,successfulli:0,suffic:4,suffix:0,suit:[1,4],suitabl:[0,4],suppli:[3,4],support:[0,2,4],sy:0,symlink:1,symmetr:[3,4],system:[0,2,3,5],t:[0,1,4],tabl:4,take:[0,1,4,5],taken:[0,4],tangerin:0,target:0,tcp:[4,5],tdata:0,teardown:[0,3],teardown_reason:0,teffect:0,tell:0,temperatur:4,ten:4,term:4,termin:3,terminolog:4,test:4,text:[0,4],tfile:0,than:[0,4],thei:[0,4],them:[0,4],thereaft:4,therefor:[4,5],thi:[0,1,2,3,4],though:4,thourough:2,thousand:4,thread:0,three:4,through:[4,5],throughout:4,throughput:[4,5],thu:4,ti:[0,4],time:[0,1,3,4],timeout:[0,3],timeout_callback:[0,3],timeoutarg:0,timestamp:4,timestr:0,tnc:[3,5],to_fil:3,todai:4,todo:4,too:[0,4],tool:5,top:4,topic:4,topolog:4,total:4,total_s:0,touch:2,toward:[0,4],tradeoff:4,tradit:4,traffic:[0,3,4],transceiv:[4,5],transfer:[0,3,4,5],transfer_s:0,transmiss:4,transmit:[0,4],transpar:4,transport:[0,2,5],transport_en:3,transport_id:3,transport_typ:3,travel:4,travers:4,treat:4,tri:0,trip:[0,3,4],trivial:5,truli:4,truncat:[3,4],truncated_hash:3,truncated_hashlength:3,trust:4,trustless:[4,5],ttime:0,ttransfer:0,tunnel:[4,5],tupl:3,two:[0,4],type:[0,2,3],typeerror:3,udp:[4,5],umsgpack:0,uncencrypt:0,underli:5,understand:[1,2],unencrypt:[0,3,4],unequivoc:4,unforg:5,uninterest:0,uniqu:[4,5],unit:0,unknown:[0,3,4],unless:[1,3,4],unlicens:4,unpack:0,unpackb:0,unsupport:3,until:[0,3,4],unwant:4,up:[0,4,5],updat:[0,3,4],upon:[0,4],urandom:5,us:[0,2,3,4],usabl:4,usag:4,usb:[4,5],useabl:4,user:[0,3,4],user_input:0,userland:5,utf:0,util:[0,1,4],utilis:[4,5],valid:[0,3,4],valu:4,valueerror:[0,3],variabl:0,varieti:[4,5],variou:[0,4],ve:1,vendor:0,veri:[2,3,4,5],verif:[3,4],verifi:[0,4],versa:5,version:3,vhf:4,via:[1,4],vice:5,view:4,visibl:0,wa:[0,3,4,5],wai:[0,1,4],wait:[0,4],want:[0,1,2,4],warrant:2,wb:0,we:[0,1,4],welcom:[2,4],well:[2,3,4,5],went:0,what:[0,1,2,3,4],whatev:4,when:[0,1,3,4],whenev:3,where:[2,3,4],whereupon:4,whether:[0,3,4],which:[0,1,3,4],who:4,whole:4,wide:[4,5],wifi:[4,5],wildcard:0,wire:[2,4,5],wish:4,within:[0,3,4],won:0,work:[4,5],world:2,would:4,write:[0,3,4],written:4,wrong:0,x:4,xenon:0,y:0,ye:4,year:4,yet:[0,4],yi:0,you:[0,1,2,3,4,5],your:[0,1,4,5],yourself:[4,5],z:0,zero:3,zi:0},titles:["Examples","Getting Started Fast","Reticulum Network Stack Documentation","API Reference","Understanding Reticulum","What is Reticulum?"],titleterms:{"1":4,"2":4,"class":3,"function":4,"public":4,"try":1,announc:[0,4],api:3,base:1,basic:4,binari:4,broadcast:0,can:5,caveat:2,content:2,current:2,destin:[3,4],develop:1,devic:5,document:2,doe:5,echo:0,emptor:2,establish:4,exampl:0,fast:1,filetransf:0,format:4,further:4,get:[1,4],goal:4,ident:[3,4],indic:2,interfac:5,introduct:4,kei:4,link:[0,3,4],minim:0,motiv:4,name:4,network:2,node:4,offer:5,packet:[3,4],particip:1,pathfind:4,prioritis:4,program:1,protocol:4,reach:4,receipt:3,refer:[3,4],resourc:[3,4],reticulum:[1,2,3,4,5],setup:4,specif:4,stack:2,start:1,statu:2,step:4,support:5,system:4,tabl:2,transport:[3,4],type:[4,5],understand:4,us:[1,5],what:5,where:5}}) \ No newline at end of file diff --git a/docs/html/understanding.html b/docs/html/understanding.html index 473d058..8d55d0f 100644 --- a/docs/html/understanding.html +++ b/docs/html/understanding.html @@ -45,47 +45,48 @@ links. It should give you an overview of how the stack works, and an understandi develop networked applications using Reticulum.

    This document is not an exhaustive source of information on Reticulum, at least not yet. Currently, the best place to go for such information is the Python reference implementation of Reticulum, along -with the API reference.

    +with the code examples and API reference. It is however an essential resource to understanding the +general principles of Reticulum, how to apply them when creating your own networks or software.

    After reading this document, you should be well-equipped to understand how a Reticulum network operates, what it can achieve, and how you can use it yourself. If you want to help out with the -development, this is also the place to start, since it will also provide a pretty clear overview of the +development, this is also the place to start, since it will provide a pretty clear overview of the sentiments and the philosophy behind Reticulum.

    -

    Motivation

    +

    Motivation

    The primary motivation for designing and implementing Reticulum has been the current lack of reliable, functional and secure minimal-infrastructure modes of digital communication. It is my belief that it is highly desirable to create a cheap and reliable way to set up a wide-range digital communication network that can securely allow exchange of information between people and machines, with no central point of authority, control, censorship or barrier to entry.

    -

    Almost all of the various networking stacks in wide use today share a common limitation, namely -that they require large amounts of coordination and trust to work. You can’t just plug in a bunch of -ethernet cables to the same switch, or turn on a number of WiFi radios, and expect such a setup to -provide a reliable platform for communication.

    -

    This need for coordination and trust inevitably leads to an environment of control, where it’s very -easy for infrastructure operators or governments to control or alter traffic.

    +

    Almost all of the various networking systems in use today share a common limitation, namely that they +require large amounts of coordination and trust to work, and to join the networks you need approval +of gatekeepers in control. This need for coordination and trust inevitably leads to an environment of +central control, where it’s very easy for infrastructure operators or governments to control or alter +traffic, and censor or persecute unwanted actors.

    Reticulum aims to require as little coordination and trust as possible. In fact, the only -“coordination” required is to know how to get connected to a Reticulum network. Since Reticulum -is medium agnostic, this could be whatever is best suited to the situation. In some cases, this might -be 1200 baud packet radio links over VHF frequencies, in other cases it might be a microwave -network using off-the-shelf radios. At the time of release of this document, the recommended setup -is using cheap LoRa radio modules with an open source firmware (see the chapter Reference System -Setup ), connected to a small computer like a Raspberry Pi. As an example, the default reference -setup provides a channel capacity of 5.4 Kbps, and a usable direct node-to-node range of around 15 -kilometers (indefinitely extendable by using multiple hops).

    +“coordination” required is to know the characteristics of physical medium carrying Reticulum traffic.

    +

    Since Reticulum is completely medium agnostic, this could be whatever is best suited to the situation. +In some cases, this might be 1200 baud packet radio links over VHF frequencies, in other cases it might +be a microwave network using off-the-shelf radios. At the time of release of this document, the +recommended setup for development and testing is using LoRa radio modules with an open source firmware +(see the section Reference System Setup), connected to a small +computer like a Raspberry Pi. As an example, the default reference setup provides a channel capacity +of 5.4 Kbps, and a usable direct node-to-node range of around 15 kilometers (indefinitely extendable +by using multiple hops).

    -

    Goals

    +

    Goals

    To be as widely usable and easy to implement as possible, the following goals have been used to guide the design of Reticulum:

    • -
      Fully useable as open source software stack

      Reticulum must be implemented, and be able to run using only open source software. This is -critical to ensuring availability, security and transparency of the system.

      +
      Fully useable as open source software stack

      Reticulum must be implemented with, and be able to run using only open source software. This is +critical to ensuring the availability, security and transparency of the system.

    • -
      Hardware layer agnosticism

      Reticulum shall be fully hardware agnostic, and should be useable over a wide range +

      Hardware layer agnosticism

      Reticulum shall be fully hardware agnostic, and shall be useable over a wide range physical networking layers, such as data radios, serial lines, modems, handheld transceivers, wired ethernet, wifi, or anything else that can carry a digital data stream. Hardware made for dedicated Reticulum use shall be as cheap as possible and use off-the-shelf components, so @@ -94,8 +95,8 @@ it can be easily replicated.

    • -
      Very low bandwidth requirements

      Reticulum should be able to function reliably over links with a data capacity as low as 1, -bps.

      +
      Very low bandwidth requirements

      Reticulum should be able to function reliably over links with a transmission capacity as low +as 1,000 bps.

    • @@ -107,12 +108,13 @@ it can be easily replicated.

    • Unlicensed use

      Reticulum shall be functional over physical communication mediums that do not require any form of license to use. Reticulum must be designed in a way, so it is usable over ISM radio -frequency bands, and can provide functional long distance links in such conditions.

      +frequency bands, and can provide functional long distance links in such conditions, for example +by connecting a modem to a PMR or CB radio, or by using LoRa or WiFi modules.

    • -
      Supplied software

      Apart from the core networking stack and API, that allows any developer to build +

      Supplied software

      Apart from the core networking stack and API, that allows a developer to build applications with Reticulum, a basic communication suite using Reticulum must be implemented and released at the same time as Reticulum itself. This shall serve both as a functional communication suite, and as an example and learning resource to others wishing @@ -121,8 +123,8 @@ to build applications with Reticulum.

    • -
      Ease of use

      The reference implementation of Reticulum is written in Python, to make it very easy to use -and understand. Any programmer with only basic experience should be able to use +

      Ease of use

      The reference implementation of Reticulum is written in Python, to make it easy to use +and understand. A programmer with only basic experience should be able to use Reticulum in their own applications.

      @@ -138,26 +140,32 @@ needs to be purchased.

    -

    Introduction & Basic Functionality

    +

    Introduction & Basic Functionality

    Reticulum is a networking stack suited for high-latency, low-bandwidth links. Reticulum is at it’s -core message oriented , but can provide connection oriented sessions. It is suited for both local -point-to-point or point-to-multipoint scenarios where alle nodes are within range of each other, as -well as scenarios where packets need to be transported over multiple hops to reach the recipient.

    +core a message oriented system. It is suited for both local point-to-point or point-to-multipoint +scenarios where alle nodes are within range of each other, as well as scenarios where packets need +to be transported over multiple hops to reach the recipient.

    Reticulum does away with the idea of addresses and ports known from IP, TCP and UDP. Instead Reticulum uses the singular concept of destinations. Any application using Reticulum as it’s networking stack will need to create one or more destinations to receive data, and know the destinations it needs to send data to.

    -

    Reticulum encrypts all data by default using public-key cryptography. Any message sent to a -destination is encrypted with that destinations public key. Reticulum also offers symmetric key -encryption for group-oriented communications, as well as unencrypted packets for broadcast -purposes, or situations where you need the communication to be in plain text. The multi-hop -transport, coordination, verification and reliability layers are fully autonomous and based on public -key cryptography.

    +

    All destinations in Reticulum are represented internally as 10 bytes, derived from truncating a full +SHA-256 hash of identifying characteristics of the destination. To users, the destination addresses +will be displayed as 10 bytes in hexadecimal representation, as in the following example: <80e29bf7cccaf31431b3>.

    +

    By default Reticulum encrypts all data using public-key cryptography. Any message sent to a +destination is encrypted with that destinations public key. Reticulum can also set up an encrypted +channel to a destination with Perfect Forward Secrecy and Initiator Anonymity using a elliptic +curve cryptography and ephemeral keys derived from a Diffie Hellman exchange on Curve25519. In +Reticulum terminology, this is called a Link.

    +

    Reticulum also offers symmetric key encryption for group-oriented communications, as well as +unencrypted packets for broadcast purposes, or situations where you need the communication to be in +plain text. The multi-hop transport, coordination, verification and reliability layers are fully +autonomous and based on public key cryptography.

    Reticulum can connect to a variety of interfaces such as radio modems, data radios and serial ports, and offers the possibility to easily tunnel Reticulum traffic over IP links such as the Internet or private IP networks.

    -

    Destinations

    +

    Destinations

    To receive and send data with the Reticulum stack, an application needs to create one or more destinations. Reticulum uses three different basic destination types, and one special:

      @@ -178,51 +186,65 @@ can by many.

    • Plain

      A plain destination type is unencrypted, and suited for traffic that should be broadcast to a -number of users, or should be readable by anyone.

      +number of users, or should be readable by anyone. Traffic to a plain destination is not encrypted.

    • -
      Link

      A link is a special destination type, that serves as an abstract channel between two single -destinations, directly connected or over multiple hops. The link also offers reliability and -more efficient encryption, and as such is useful even when nodes are directly connected.

      +
      Link

      A link is a special destination type, that serves as an abstract channel to a single +destination, directly connected or over multiple hops. The link also offers reliability and +more efficient encryption, forward secrecy, initiator anonymity, and as such can be useful even +when a node is directly reachable.

    -

    Destination Naming

    +

    Destination Naming

    Destinations are created and named in an easy to understand dotted notation of aspects , and represented on the network as a hash of this value. The hash is a SHA-256 truncated to 80 bits. The -top level aspect should always be the a unique identifier for the application using the destination. +top level aspect should always be a unique identifier for the application using the destination. The next levels of aspects can be defined in any way by the creator of the application. For example, -a destination for a messaging application could be made up of the application name and a username, -and look like this:

    -
    name: simplemessenger.someuser hash: 2a7ddfab5213f916dea
    +a destination for a environmental monitoring application could be made up of the application name, a
    +device type and measurement type, like this:

    +
    app name  : environmentlogger
    +aspects   : remotesensor, temperature
    +
    +full name : environmentlogger.remotesensor.temperature
    +hash      : fa7ddfab5213f916dea
     

    For the single destination, Reticulum will automatically append the associated public key as a destination aspect before hashing. This is done to ensure only the correct destination is reached, since anyone can listen to any destination name. Appending the public key ensures that a given packet is only directed at the destination that holds the corresponding private key to decrypt the -packet. It is important to understand that anyone can use the destination name -simplemessenger.myusername , but each person that does so will still have a different destination -hash, because their public keys will differ. In actual use of single destination naming, it is advisable -not to use any uniquely identifying features in aspect naming, though. In the simple messenger -example, when using single destinations, we would instead use a destination naming scheme such -as simplemessenger.user where appending the public key expands the destination into a uniquely -identifying one.

    -

    To recap, the destination types should be used in the following situations:

    +packet.

    +

    Take note! There is a very important concept to understand here:

    +
      +
    • Anyone can use the destination name environmentlogger.remotesensor.temperature

    • +
    • Each destination that does so will still have a unique destination hash, and thus be uniquely +addressable, because their public keys will differ.

    • +
    +

    In actual use of single destination naming, it is advisable not to use any uniquely identifying +features in aspect naming. Aspect names should be general terms describing what kind of destination +is represented. The uniquely identifying aspect is always acheived by the appending the public key, +which expands the destination into a uniquely identifyable one.

    +

    Any destination on a Reticulum network can be addressed and reached simply by knowning its +destination hash (and public key, but if the public key is not known, it can be requested from the +network simply by knowing the destination hash). The use of app names and aspects makes it easy to +structure Reticulum programs and makes it possible to filter what information and data your program +receives.

    +

    To recap, the different destination types should be used in the following situations:

    • -
      Single

      When private communication between two endpoints is needed. Supports routing.

      +
      Single

      When private communication between two endpoints is needed. Supports multiple hops.

    • Group

      When private communication between two or more endpoints is needed. More efficient in -data usage than single destinations. Supports routing indirectly, but must first be established -through a single destination.

      +data usage than single destinations. Supports multiple hops indirectly, but must first be +established through a single destination.

    • @@ -234,14 +256,17 @@ through a single destination.

    To communicate with a single destination, you need to know it’s public key. Any method for obtaining the public key is valid, but Reticulum includes a simple mechanism for making other -nodes aware of your destinations public key, called the announce.

    -

    Note that this information could be shared and verified in many other ways, and that it is therefore -not required to use the announce functionality, although it is by far the easiest, and should probably -be used if you are not confident in how to verify public keys and signatures manually.

    +nodes aware of your destinations public key, called the announce. It is also possible to request +an unknown public key from the network, as all participating nodes serve as a distributed ledger +of public keys.

    +

    Note that public key information can be shared and verified in many other ways than using the +built-in methodology, and that it is therefore not required to use the announce/request functionality. +It is by far the easiest though, and should definitely be used if there is not a good reason for +doing it differently.

    -

    Public Key Announcements

    +

    Public Key Announcements

    An announce will send a special packet over any configured interfaces, containing all needed information about the destination hash and public key, and can also contain some additional, application specific data. The entire packet is signed by the sender to ensure authenticity. It is not @@ -262,11 +287,11 @@ the aspect names of the destination. These are intentionally left out to save ba will be implicit in almost all cases. If a destination name is not entirely implicit, information can be included in the application specific data part that will allow the receiver to infer the naming.

    It is important to note that announcements will be forwarded throughout the network according to a -certain pattern. This will be detailed later. Seeing how single destinations are always tied to a -private/public key pair leads us to the next topic.

    +certain pattern. This will be detailed later.

    +

    Seeing how single destinations are always tied to a private/public key pair leads us to the next topic.

    -
    -

    Identities

    +
    +

    Identities

    In Reticulum, an identity does not necessarily represent a personal identity, but is an abstraction that can represent any kind of verified entity. This could very well be a person, but it could also be the control interface of a machine, a program, robot, computer, sensor or something else entirely. In @@ -283,173 +308,184 @@ reach the user. In such a case it is of great importance to store the user’s i privately.

    -

    Getting Further

    +

    Getting Further

    The above functions and principles form the core of Reticulum, and would suffice to create functional networked applications in local clusters, for example over radio links where all interested -nodes can hear each other. But to be truly useful, we need a way to go further. In the next chapter, -two concepts that allow this will be introduced, paths and resources.

    +nodes can directly hear each other. But to be truly useful, we need a way to direct traffic over multiple +hops in the network. In the next sections, two concepts that allow this will be introduced, paths and +links.

    -

    Reticulum Transport

    -

    I have purposefully avoided the term routing until now, and will continue to do so, because the -current methods of routing used in IP based networks are fundamentally incompatible for the link -types that Reticulum was designed to handle. These routing methodologies assume trust at the -physical layer. Since Reticulum is designed to run over open radio spectrum, no such trust exists. -Furthermore, existing routing protocols like BGP or OSPF carry too much overhead to be -practically useable over bandwidth-limited, high-latency links.

    +

    Reticulum Transport

    +

    The term routing has been purposefully avoided until now. The current methods of routing used in IP-based +networks are fundamentally incompatible with the physical link types that Reticulum was designed to handle. +These routing methodologies assume trust at the physical layer, and often needs a lot more bandwidth than +Reticulum can assume is available.

    +

    Since Reticulum is designed to run over open radio spectrum, no such trust exists, and bandwidth is often +very limited. Existing routing protocols like BGP or OSPF carry too much overhead to be practically +useable over bandwidth-limited, high-latency links.

    To overcome such challenges, Reticulum’s Transport system uses public-key cryptography to implement the concept of paths that allow discovery of how to get information to a certain -destination, and resources that help alleviate congestion and make reliable communication more -efficient and less bandwidth-hungry.

    -
    -

    Threading a Path

    +destination, and resources that help make reliable data transfer more efficient.

    +
    +

    Reaching the Destination

    In networks with changing topology and trustless connectivity, nodes need a way to establish -verified connectivity with each other. To do this, the following process is employed:

    -
      -
    • -
      First, the node that wishes to establish connectivity will send out a special packet, that

      traverses the network and locates the desired destination. Along the way, the nodes that -forward the packet will take note of this link request.

      -
      -
      +verified connectivity with each other. Since the network is assumed to be trustless, Reticulum +must provide a way to guarantee that the peer you are communicating with is actually who you +expect. To do this, the following process is employed:

      +
        +
      • +
        First, the node that wishes to establish connectivity will send out a special packet, that +traverses the network and locates the desired destination. Along the way, the nodes that +forward the packet will take note of this link request.
        +
      • -
      • -
        Second, if the destination accepts the link request , it will send back a packet that proves the

        authenticity of it’s identity (and the receipt of the link request) to the initiating node. All +

      • +
        Second, if the destination accepts the link request , it will send back a packet that proves the +authenticity of it’s identity (and the receipt of the link request) to the initiating node. All nodes that initially forwarded the packet will also be able to verify this proof, and thus -accept the validity of the link throughout the network.

        -
      • -
        +accept the validity of the link throughout the network.
    +
    -
  • -
    When the validity of the link has been accepted by forwarding nodes, these nodes will

    remember the link , and it can subsequently be used by referring to a hash representing it.

    -
    -
    +
  • +
    When the validity of the link has been accepted by forwarding nodes, these nodes will +remember the link , and it can subsequently be used by referring to a hash representing it.
    +
  • -
  • -
    As a part of the link request , a Diffie-Hellman key exchange takes place, that sets up an

    efficient symmetrically encrypted tunnel between the two nodes, using elliptic curve +

  • +
    As a part of the link request , a Diffie-Hellman key exchange takes place, that sets up an +efficient symmetrically encrypted tunnel between the two nodes, using elliptic curve cryptography. As such, this mode of communication is preferred, even for situations when nodes can directly communicate, when the amount of data to be exchanged numbers in the -tens of packets.

    -
  • -
    +tens of packets.
  • +
    -
  • -
    When a link has been set up, it automatically provides message receipt functionality, so the

    sending node can obtain verified confirmation that the information reached the intended -recipient.

    -
    -
    +
  • +
    When a link has been set up, it automatically provides message receipt functionality, so the +sending node can obtain verified confirmation that the information reached the intended +recipient.
    +
  • In a moment, we will discuss the specifics of how this methodology is implemented, but let’s first recap what purposes this serves. We first ensure that the node answering our request is actually the one we want to communicate with, and not a malicious actor pretending to be so. At the same time we establish an efficient encrypted channel. The setup of this is relatively cheap in terms of -bandwidth, so it can be used just for a short exchange, and then recreated as needed, which will also

    -

    rotate encryption keys (keys can also be rotated over an existing path), but the link can also be kept -alive for longer periods of time, if this is more suitable to the application. The amount of bandwidth -used on keeping a link open is practically negligible. The procedure also inserts the link id , a hash -calculated from the link request packet, into the memory of forwarding nodes, which means that the -communicating nodes can thereafter reach each other simply by referring to this link id.

    -

    Step 1, pathfinding

    +bandwidth, so it can be used just for a short exchange, and then recreated as needed, which will also +rotate encryption keys, but the link can also be kept alive for longer periods of time, if this is +more suitable to the application. The amount of bandwidth used on keeping a link open is practically +negligible. The procedure also inserts the link id , a hash calculated from the link request packet, +into the memory of forwarding nodes, which means that the communicating nodes can thereafter reach each +other simply by referring to this link id.

    +
    +

    Step 1: Pathfinding

    The pathfinding method builds on the announce functionality discussed earlier. When an announce is sent out by a node, it will be forwarded by any node receiving it, but according to some specific rules:

    -
      -
    • If this announce has already been received before, ignore it.

    • -
    • -
      Record into a table which node the announce was received from, and how many times in

      total it has been retransmitted to get here.

      -
      -
      +
        +
      • +
        If this announce has already been received before, ignore it.
        +
      • -
      • -
        If the announce has been retransmitted m+1 times, it will not be forwarded. By default, m is

        set to 18.

        -
        -
        +
      • +
        Record into a table which node the announce was received from, and how many times in +total it has been retransmitted to get here.
        +
      • -
      • -
        The announce will be assigned a delay d = ch seconds, where c is a decay constant, by

        default 2, and h is the amount of times this packet has already been forwarded.

        -
        -
        +
      • +
        If the announce has been retransmitted m+1 times, it will not be forwarded. By default, m is +set to 18.
        +
      • -
      • The packet will be given a priority p = 1/d.

      • -
      • -
        If at least d seconds has passed since the announce was received, and no other packets with a

        priority higher than p are waiting in the queue (see Packet Prioritisation), and the channel is -not utilized by other traffic, the announce will be forwarded.

        -
        -
        +
      • +
        The announce will be assigned a delay d = ch seconds, where c is a decay constant, by +default 2, and h is the amount of times this packet has already been forwarded.
        +
      • -
      • -
        If no other nodes are heard retransmitting the announce with a greater hop count than when

        it left this node, transmitting it will be retried r times. By default, r is set to 2. Retries follow -same rules as above, with the exception that it must wait for at least d = ch+1 + t seconds, ie., +

      • +
        The packet will be given a priority p = 1/d.
        +
        +
      • +
      • +
        If at least d seconds has passed since the announce was received, and no other packets with a +priority higher than p are waiting in the queue (see Packet Prioritisation), and the channel is +not utilized by other traffic, the announce will be forwarded.
        +
        +
      • +
      • +
        If no other nodes are heard retransmitting the announce with a greater hop count than when +it left this node, transmitting it will be retried r times. By default, r is set to 2. Retries follow +same rules as above, with the exception that it must wait for at least d = ch+1 + t seconds, ie., the amount of time it would take the next node to retransmit the packet. By default, t is set to -10.

        -
      • -
        +10.
    +
    -
  • -
    If a newer announce from the same destination arrives, while an identical one is already in

    the queue, the newest announce is discarded. If the newest announce contains different +

  • +
    If a newer announce from the same destination arrives, while an identical one is already in +the queue, the newest announce is discarded. If the newest announce contains different application specific data, it will replace the old announce, but will use d and p of the old -announce.

    -
  • -
    +announce.
  • +

    Once an announce has reached a node in the network, any other node in direct contact with that node will be able to reach the destination the announce originated from, simply by sending a packet addressed to that destination. Any node with knowledge of the announce will be able to direct the packet towards the destination by looking up the next node with the shortest amount of hops to the -destination. The specifics of this process is detailed in Path Calculation.

    +destination.

    According to these rules and default constants, an announce will propagate throughout the network -in a predictable way. In an example network utilising the default constants, and with an average link

    -

    distance of Lavg = 15 kilometers, an announce will be able to propagate outwards to a radius of 180 +in a predictable way. In an example network utilising the default constants, and with an average link +distance of Lavg = 15 kilometers, an announce will be able to propagate outwards to a radius of 180 kilometers in 34 minutes, and a maximum announce radius of 270 kilometers in approximately 3 -days. Methods for overcoming the distance limitation of m * Lavg will be introduced later in this -chapter.

    -

    Step 2, link establishment

    +days.

    + + + -
  • -
    When the destination receives the link request packet, it will decide whether to accept the

    request. If it is accepted, it will create a special packet called a proof. A proof is a simple +

  • +
    When the destination receives the link request packet, it will decide whether to accept the +request. If it is accepted, it will create a special packet called a proof. A proof is a simple construct, consisting of a truncated hash of the message that needs to be proven, and a signature (made by the destination’s private key) of this hash. This proof effectively verifies that the intended recipient got the packet, and also serves to verify the discovered path through the network. Since the proof hash matches the path id in the intermediary nodes’ -path tables , the intermediary nodes can forward the proof all the way back to the source.

    -
  • -
    +path tables , the intermediary nodes can forward the proof all the way back to the source. +
  • -
  • -
    When the source receives the proof , it will know unequivocally that a verified path has been

    established to the destination, and that information can now be exchanged reliably and -securely.

    -
    -
    +
  • +
    When the source receives the proof , it will know unequivocally that a verified path has been +established to the destination, and that information can now be exchanged reliably and +securely.
    +
  • It’s important to note that this methodology ensures that the source of the request does not need to @@ -463,16 +499,10 @@ of Reticulum, such a retransmission does not need to travel the entire length of If a packet is lost on the 8th hop of a 12 hop path, it can be fetched from the last hop that received it reliably.

    -
    -

    Crossing Continents

    -

    When a packet needs to travel farther than local network topology knowledge stretches, a system of -geographical or topological hinting is used to direct the packet towards a network segment with -direct knowledge of the intended destination. This functionality is currently left out of the protocol -for simplicity of testing other parts, but will be activated in a future release. For more information -on when, refer to the roadmap on the website.

    -
    -

    Resourceful Memory

    +
    +

    Resources

    +

    TODO: Write

    In traditional networks, large amounts of data is rapidly exchanged with very low latency. Links of several thousand kilometers will often only have round-trip latency in the tens of milliseconds, and as such, traditional protocols are often designed to not store any transmitted data at intermediary @@ -501,7 +531,7 @@ code.

    -

    Reference System Setup

    +

    Reference System Setup

    This section will detail the recommended Reference System Setup for Reticulum. It is important to note that Reticulum is designed to be usable over more or less any medium that allows you to send and receive data in a digital form, and satisfies some very low minimum requirements. The @@ -541,13 +571,12 @@ into the future. The current Reference System Setup is as follows:

  • Channel Access Device

    A data radio consisting of a LoRa radio module, and a microcontroller with open source firmware, that can connect to host devices via USB. It operates in either the 430, 868 or 900 -MHz frequency bands. More details on the exact parts and how to get/make one can be -found on the website.

    +MHz frequency bands. More details can be found on the RNode Page.

  • -
    Host device

    Any computer device running Linux and Python. A Raspberry Pi with Raspbian is +

    Host device

    Any computer device running Linux and Python. A Raspberry Pi with a Debian based OS is recommended.

    @@ -561,39 +590,118 @@ operating system.

    It is very important to note, that the reference channel access device does not use the LoRaWAN standard, but uses a custom MAC layer on top of the plain LoRa modulation! As such, you will -need a plain LoRa radio module connected to an MCU with the correct Reticulum firmware. Full -details on how to get or make such a device is available on the website.

    -

    With the current reference setup, it should be possible to get on a Reticulum network for around 70$ -even if you have none of the hardware already.

    +need a plain LoRa radio module connected to an MCU with the correct firmware. Full details on how to +get or make such a device is available on the RNode Page.

    +

    With the current reference setup, it should be possible to get on a Reticulum network for around 100$ +even if you have none of the hardware already, and need to purchase everything.

  • -

    Protocol Specifics

    +

    Protocol Specifics

    This chapter will detail protocol specific information that is essential to the implementation of Reticulum, but non critical in understanding how the protocol works on a general level. It should be treated more as a reference than as essential reading.

    Node Types

    Currently Reticulum defines two node types, the Station and the Peer. A node is a station if it fixed -in one place, and if it is intended to be kept online at all times. Otherwise the node is a peer. This -distinction is made by the user configuring the node, and is used to determine what nodes on the +in one place, and if it is intended to be kept online most of the time. Otherwise the node is a peer. +This distinction is made by the user configuring the node, and is used to determine what nodes on the network will help forward traffic, and what nodes rely on other nodes for connectivity.

    +

    If a node is a Peer it should be given the configuration directive enable_transport = No.

    +

    If it is a Station, it should be given the configuration directive enable_transport = Yes.

    Packet Prioritisation

    -

    The packet prioritisation algorithms are subject to rapid change at the moment, and for now, they -are not documented here. See the reference implementation for more info on how this functionality -works.

    -
    -
    -

    Path Calculation

    -

    The path calculation algorithms are subject to rapid change at the moment, and for now, they are -not documented here. See the reference implementation for more info on how this functionality -works.

    +

    Currently, Reticulum is completely priority-agnostic regarding general traffic. All traffic is handled +on a first-come, first-serve basis. Announce re-transmission are handled according to the re-transmission +times and priorities described earlier in this chapter.

    +

    It is possible that a prioritisation engine could be added to Reticulum in the future, but in +the light of Reticulums goal of equal access, doing so would need to be the subject of careful +investigation of the consequences first.

    Binary Packet Format

    -

    The binary packet format is subject to rapid change at the moment, and for now, it is not -documented here. See the reference implementation for the specific details on this topic.

    +
    == Reticulum Wire Format ======
    +
    +A Reticulum packet is composed of the following fields:
    +
    +[HEADER 2 bytes] [ADDRESSES 10/20 bytes] [CONTEXT 1 byte] [DATA 0-477 bytes]
    +
    +* The HEADER field is 2 bytes long.
    +  * Byte 1: [Header Type], [Propagation Type], [Destination Type] and [Packet Type]
    +  * Byte 2: Number of hops
    +
    +* The ADDRESSES field contains either 1 or 2 addresses.
    +  * Each address is 10 bytes long.
    +  * The Header Type flag in the HEADER field determines
    +    whether the ADDRESSES field contains 1 or 2 addresses.
    +  * Addresses are Reticulum hashes truncated to 10 bytes.
    +
    +* The CONTEXT field is 1 byte.
    +  * It is used by Reticulum to determine packet context.
    +
    +* The DATA field is between 0 and 477 bytes.
    +  * It contains the packets data payload.
    +
    +Header Types
    +-----------------
    +type 1          00  Two byte header, one 10 byte address field
    +type 2          01  Two byte header, two 10 byte address fields
    +type 3          10  Reserved
    +type 4          11  Reserved
    +
    +
    +Propagation Types
    +-----------------
    +broadcast       00
    +transport       01
    +reserved        10
    +reserved        11
    +
    +
    +Destination Types
    +-----------------
    +single          00
    +group           01
    +plain           10
    +link            11
    +
    +
    +Packet Types
    +-----------------
    +data            00
    +announce        01
    +link request    10
    +proof           11
    +
    +
    ++- Packet Example -+
    +
    +   HEADER FIELD             ADDRESSES FIELD             CONTEXT FIELD  DATA FIELD
    + _______|_______   ________________|________________   ________|______   __|_
    +|               | |                                 | |               | |    |
    +01010000 00000100 [ADDR1, 10 bytes] [ADDR2, 10 bytes] [CONTEXT, 1 byte] [DATA]
    + | | | |    |
    + | | | |    +-- Hops             = 4
    + | | | +------- Packet Type      = DATA
    + | | +--------- Destination Type = SINGLE
    + | +----------- Propagation Type = TRANSPORT
    + +------------- Header Type      = HEADER_2 (two byte header, two address fields)
    +
    +
    + +- Packet Example -+
    +
    +   HEADER FIELD    ADDRESSES FIELD    CONTEXT FIELD  DATA FIELD
    + _______|_______   _______|_______   ________|______   __|_
    +|               | |               | |               | |    |
    +00000000 00000111 [ADDR1, 10 bytes] [CONTEXT, 1 byte] [DATA]
    + | | | |    |
    + | | | |    +-- Hops             = 7
    + | | | +------- Packet Type      = DATA
    + | | +--------- Destination Type = SINGLE
    + | +----------- Propagation Type = BROADCAST
    + +------------- Header Type      = HEADER_1 (two byte header, one address field)
    +
    +
    @@ -616,21 +724,23 @@ documented here. See the reference implementation for the specific details on th
  • Public Key Announcements
  • -
  • Identities
  • +
  • Identities
  • Getting Further
  • Reticulum Transport
  • Reference System Setup
  • Protocol Specifics
  • diff --git a/docs/html/whatis.html b/docs/html/whatis.html index 524c4cc..6d8f926 100644 --- a/docs/html/whatis.html +++ b/docs/html/whatis.html @@ -52,7 +52,7 @@
  • Coordination-less globally unique adressing and identification

  • Fully self-configuring multi-hop routing

  • Asymmetric RSA encryption and signatures as basis for all communication

  • -
  • Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on the SECP256R1 curve)

  • +
  • Perfect Forward Secrecy on links with ephemereal Elliptic Curve Diffie-Hellman keys (on Curve25519)

  • Reticulum uses the Fernet specification for encryption on links and to group destinations

    • AES-128 in CBC mode with PKCS7 padding