Compare commits

..

38 Commits

Author SHA1 Message Date
Mark Qvist a70bd44426 Prepare release 2026-04-21 18:54:31 +02:00
Mark Qvist 8c082b2fcc Fixed path state potentially being applied before path table entry exists. 2026-04-21 18:49:03 +02:00
Mark Qvist 1732cac806 Updated makefile 2026-04-21 17:10:27 +02:00
Mark Qvist e1340e87eb Prepare release 2026-04-21 17:02:37 +02:00
Mark Qvist e9bfef2131 Cleanup 2026-04-21 16:55:59 +02:00
Mark Qvist b408699e65 Periodically clean known destinations data based on local relevance 2026-04-21 13:21:23 +02:00
Mark Qvist 3d1c508868 Improved BackboneInterface error handling 2026-04-21 00:24:00 +02:00
Mark Qvist 84e0746c9c Updated version 2026-04-20 23:49:24 +02:00
Mark Qvist b5658c4865 Keep track of which known destinations are actually in use, so irrelevant destination data can be cleaned 2026-04-20 23:48:57 +02:00
Mark Qvist d413a4bc53 Improved resource transfer timing calculations 2026-04-20 23:44:55 +02:00
Mark Qvist ce5ab902b6 Updated docs 2026-04-20 11:38:14 +02:00
Mark Qvist 294408b0bb Run non-background data persist synchronously 2026-04-19 01:32:12 +02:00
Mark Qvist 53372fbe4c Updated docs 2026-04-18 17:27:42 +02:00
Mark Qvist 7fdac2118b Prepare release 2026-04-18 16:07:38 +02:00
Mark Qvist 1dbf78ed71 Updated changelog 2026-04-18 16:06:14 +02:00
Mark Qvist c9101a0c21 Ensure loop-originating closures have variables captured at iteration-time. Thanks @taprootmx! 2026-04-18 15:36:33 +02:00
Mark Qvist 2e6264c04b Updated changelog 2026-04-18 15:24:29 +02:00
Mark Qvist e0aa46ba22 Improved gracious transport data persist handling 2026-04-18 14:50:45 +02:00
Mark Qvist 8093c3cd2c Added local destinations lookup map 2026-04-17 11:39:14 +02:00
Mark Qvist c6778e4e29 Improved transport tunnel handling. Improved memory consumption. Fixed disk I/O bound thread execution time starvation on cache management jobs. 2026-04-17 00:07:07 +02:00
Mark Qvist c77548d299 Updated docs 2026-04-15 18:54:54 +02:00
Mark Qvist 26d435ea64 Updated version 2026-04-15 18:48:59 +02:00
Mark Qvist c3f0d98e41 Refactoring work for free-threaded transport I/O. Added ingress control bypass on pending path requests. 2026-04-15 18:48:17 +02:00
Mark Qvist 3c50f4aee9 Updated logging 2026-04-15 12:06:15 +02:00
Mark Qvist 4a930ba82a Fixed invalid EPOLL modification error handler 2026-04-15 12:04:26 +02:00
Mark Qvist 866e63f0fe Apply patch from K8: Fix IFAC for autoconnected, discovered interfaces. 2026-04-15 10:37:41 +02:00
Mark Qvist d461cfa8ce Updated manual 2026-04-15 10:32:41 +02:00
Mark Qvist 18708636fb Updated manual 2026-04-13 20:38:55 +02:00
Mark Qvist 1901cca2f3 Prepare release 2026-04-13 11:28:22 +02:00
Mark Qvist 344019f108 Prepare release 2026-04-13 11:27:46 +02:00
Mark Qvist e22a8021d3 Copy on known destinations persist 2026-04-13 11:12:12 +02:00
Mark Qvist 111c9c0ed0 Fixed missing configuration entry generation for discovered I2P interfaces. Improved interface discovery validation. 2026-04-12 19:57:34 +02:00
Mark Qvist 2445d18149 Fixed invalid ingress control burst activation and subsequent path resolution failure due to incorrect announce frequency calculation 2026-04-12 18:39:06 +02:00
Mark Qvist 739523d559 Cancel pending resource segments recursively 2026-04-12 15:35:36 +02:00
Mark Qvist 23c0a493b1 Refactoring work for free-threaded transport I/O 2026-04-12 14:55:42 +02:00
Mark Qvist fa353fb0b3 Refactored transport jobs for free-threaded implementation 2026-04-12 13:33:15 +02:00
Mark Qvist 9f817bd918 Cleanup 2026-04-12 12:20:29 +02:00
Mark Qvist 2e5480a6bd Cleanup 2026-04-12 11:20:51 +02:00
46 changed files with 1429 additions and 1018 deletions
+92
View File
@@ -1,3 +1,95 @@
### 2026-04-21: RNS 1.1.8
This maintenance release fixes a critical bug in path state management, that could result in significant path convergence degradation under certain conditions.
**Changes**
- Fixed path state potentially being applied before path table entry exists, causing worse paths to be selected.
**Release Hashes**
```
9cf728e9e9a9fe113e4ac14e6b833f7ee65feedf8468e6ab94a261bf205f2632 rns-1.1.8-py3-none-any.whl
407dc3975335e9eabaaddb7ed1dc75fc3a1b8d24a7207e740797440c2ad0b3e5 rnspure-1.1.8-py3-none-any.wh
```
**Release Signatures**
Release artifacts include `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`:
```sh
rnid -i bc7291552be7a58f361522990465165c -V rns-1.1.7-py3-none-any.whl.rsg
```
### 2026-04-21: RNS 1.1.7
**Changes**
- Added periodic known destination data cleaning based on local relevance.
- Improved resource transfer sequencing timing calculations and reliability.
- Improved BackboneInterface error handling on EPOLL errors.
- Ensured non-background data persist runs synchronously.
**Release Hashes**
```
4d9702c5d9bb8a3c8b94766cb51cccad5afd78d615af9a6b146730347044e6f0 rns-1.1.7-py3-none-any.whl
172dede7656b41b85e4319354ed04649b518e58c54586da7e443579c620a0a5b rnspure-1.1.7-py3-none-any.whl
```
**Release Signatures**
Release artifacts include `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`:
```sh
rnid -i bc7291552be7a58f361522990465165c -V rns-1.1.7-py3-none-any.whl.rsg
```
### 2026-04-18: RNS 1.1.6
**Changes**
- Improved transport memory consumption.
- Improved transport tunnel handling.
- Improved gracious transport data persist handling.
- Added ingress control bypass for pending path requests.
- Added local destinations lookup map for better transport efficiency to local destinations.
- Fixed disk I/O bound thread execution time starvation on cache management jobs.
- Fixed invalid EPOLL modification error handler.
- Fixed incorrect default IFAC size for autoconnected, discovered interfaces. Thanks @taprootmx!
- Ensure loop-originating closures have variables captured at iteration-time. Thanks @taprootmx!
**Release Hashes**
```
2ce4451668f8c464295cc269188c232e7805ddd618ec0135550a5e6809df5de0 rns-1.1.6-py3-none-any.whl
ba3e541e69a2f4892177383c8ec4e7d172d298546317e08270928c0163865aa3 rnspure-1.1.6-py3-none-any.wh
```
**Release Signatures**
Release artifacts include `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`:
```sh
rnid -i bc7291552be7a58f361522990465165c -V rns-1.1.6-py3-none-any.whl.rsg
```
### 2026-04-13: RNS 1.1.5
**Changes**
- Initial refactoring work for free-threaded transport I/O.
- Improved interface discovery validation.
- Fixed invalid ingress control burst activation and subsequent path resolution failure due to incorrect announce frequency calculation.
- Fixed missing configuration entry generation for discovered I2P interfaces.
- Fixed resource transfer cancellation failing on in-flight split resource transfers.
- Fixed ingress control configuration not inheriting down to spawned interfaces on some interface types.
**Release Hashes**
```
28f39ad97ef307a1e270b91ef19db07d8e1a7bbc8628c478303725894c64deff rns-1.1.5-py3-none-any.whl
1a90db16d2cff4ad909b44baf9b4fd0177da2ed545cdb9cfb2c51423707b49e9 rnspure-1.1.5-py3-none-any.whl
```
**Release Signatures**
Release artifacts include `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`:
```sh
rnid -i bc7291552be7a58f361522990465165c -V rns-1.1.5-py3-none-any.whl.rsg
```
#
### 2026-03-12: RNS 1.1.4
**Changes**
+12 -3
View File
@@ -61,9 +61,18 @@ release: test remove_symlinks build_sdist build_wheel build_pure_wheel documenta
debug: remove_symlinks build_wheel build_pure_wheel create_symlinks
upload:
@echo Ready to publish release, hit enter to continue
upload: upload-rns upload-rnspure
upload-rns:
@echo Ready to publish rns release, hit enter to continue
@read VOID
@echo Uploading to PyPi...
twine upload dist/*
twine upload dist/rns-*.whl dist/rns-*.tar.gz
@echo Release published
upload-rnspure:
@echo Ready to publish rnspure release, hit enter to continue
@read VOID
@echo Uploading to PyPi...
twine upload dist/rnspure-*.whl
@echo Release published
+13 -24
View File
@@ -295,33 +295,26 @@ class Destination:
app_data = returned_app_data
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash+ratchet
if app_data != None:
signed_data += app_data
if app_data != None: signed_data += app_data
signature = self.identity.sign(signed_data)
announce_data = self.identity.get_public_key()+self.name_hash+random_hash+ratchet+signature
if app_data != None:
announce_data += app_data
if app_data != None: announce_data += app_data
self.path_responses[tag] = [time.time(), announce_data]
if path_response:
announce_context = RNS.Packet.PATH_RESPONSE
else:
announce_context = RNS.Packet.NONE
if path_response: announce_context = RNS.Packet.PATH_RESPONSE
else: announce_context = RNS.Packet.NONE
if ratchet:
context_flag = RNS.Packet.FLAG_SET
else:
context_flag = RNS.Packet.FLAG_UNSET
if ratchet: context_flag = RNS.Packet.FLAG_SET
else: context_flag = RNS.Packet.FLAG_UNSET
announce_packet = RNS.Packet(self, announce_data, RNS.Packet.ANNOUNCE, context = announce_context,
attached_interface = attached_interface, context_flag=context_flag)
if send:
announce_packet.send()
else:
return announce_packet
if send: announce_packet.send()
else: return announce_packet
def accepts_links(self, accepts = None):
"""
@@ -330,13 +323,10 @@ class Destination:
:param accepts: If ``True`` or ``False``, this method sets whether the destination accepts incoming link requests. If not provided or ``None``, the method returns whether the destination currently accepts link requests.
:returns: ``True`` or ``False`` depending on whether the destination accepts incoming link requests, if the *accepts* parameter is not provided or ``None``.
"""
if accepts == None:
return self.accept_link_requests
if accepts == None: return self.accept_link_requests
if accepts:
self.accept_link_requests = True
else:
self.accept_link_requests = False
if accepts: self.accept_link_requests = True
else: self.accept_link_requests = False
def set_link_established_callback(self, callback):
"""
@@ -421,8 +411,7 @@ class Destination:
else:
if packet.packet_type == RNS.Packet.DATA:
if self.callbacks.packet != None:
try:
self.callbacks.packet(plaintext, packet)
try: self.callbacks.packet(plaintext, packet)
except Exception as e:
RNS.log("Error while executing receive callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
+18 -10
View File
@@ -106,6 +106,10 @@ class InterfaceAnnouncer():
LONGITUDE: interface.discovery_longitude,
HEIGHT: interface.discovery_height}
if interface_type == "TCPClientInterface" and not interface.kiss_framing:
RNS.log(f"Invalid interface discovery configuration for {interface}, aborting discovery announce", RNS.LOG_ERROR)
return None
if interface_type in ["BackboneInterface", "TCPServerInterface"]:
reachable_on = self.sanitize(interface.reachable_on)
@@ -337,18 +341,19 @@ class InterfaceAnnounceHandler:
RNS.log(f"An error occurred while trying to decode discovered interface. The contained exception was: {e}", RNS.LOG_DEBUG)
class InterfaceDiscovery():
THRESHOLD_UNKNOWN = 24*60*60
THRESHOLD_STALE = 3*24*60*60
THRESHOLD_REMOVE = 7*24*60*60
THRESHOLD_UNKNOWN = 24*60*60
THRESHOLD_STALE = 3*24*60*60
THRESHOLD_REMOVE = 7*24*60*60
MONITOR_INTERVAL = 5
DETACH_THRESHOLD = 12
MONITOR_INTERVAL = 5
DETACH_THRESHOLD = 12
STATUS_STALE = 0
STATUS_UNKNOWN = 100
STATUS_AVAILABLE = 1000
STATUS_CODE_MAP = {"available": STATUS_AVAILABLE, "unknown": STATUS_UNKNOWN, "stale": STATUS_STALE}
AUTOCONNECT_TYPES = ["BackboneInterface", "TCPServerInterface"]
STATUS_STALE = 0
STATUS_UNKNOWN = 100
STATUS_AVAILABLE = 1000
STATUS_CODE_MAP = {"available": STATUS_AVAILABLE, "unknown": STATUS_UNKNOWN, "stale": STATUS_STALE}
AUTOCONNECT_TYPES = ["BackboneInterface", "TCPServerInterface"]
DISCOVERABLE_TYPES = ["BackboneInterface", "TCPServerInterface", "I2PInterface", "RNodeInterface", "WeaveInterface", "KISSInterface"]
def __init__(self, required_value=InterfaceAnnouncer.DEFAULT_STAMP_VALUE, callback=None, discover_interfaces=True):
if not required_value: required_value = InterfaceAnnouncer.DEFAULT_STAMP_VALUE
@@ -385,6 +390,7 @@ class InterfaceDiscovery():
if heard_delta > self.THRESHOLD_REMOVE: should_remove = True
elif discovery_sources and not "network_id" in info: should_remove = True
elif discovery_sources and not bytes.fromhex(info["network_id"]) in discovery_sources: should_remove = True
elif not "type" in info or ("type" in info and not info["type"] in self.DISCOVERABLE_TYPES): should_remove = True
elif "reachable_on" in info:
if not (is_ip_address(info["reachable_on"]) or is_hostname(info["reachable_on"])): should_remove = True
@@ -421,6 +427,8 @@ class InterfaceDiscovery():
value = info["value"]
interface_type = info["type"]
discovery_hash = info["discovery_hash"]
discovered_type = info["type"]
if not discovered_type in self.DISCOVERABLE_TYPES: return
hops = info["hops"]; ms = "" if hops == 1 else "s"
filename = RNS.hexrep(discovery_hash, delimit=False)
filepath = os.path.join(self.storagepath, filename)
+131 -33
View File
@@ -94,17 +94,25 @@ class Identity:
known_ratchets = {}
ratchet_persist_lock = threading.Lock()
known_destinations_lock = threading.Lock()
@staticmethod
def remember(packet_hash, destination_hash, public_key, app_data = None):
if len(public_key) != Identity.KEYSIZE//8:
raise TypeError("Can't remember "+RNS.prettyhexrep(destination_hash)+", the public key size of "+str(len(public_key))+" is not valid.", RNS.LOG_ERROR)
else:
Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data]
with Identity.known_destinations_lock:
if not destination_hash in Identity.known_destinations:
Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data, 0]
else:
entry = Identity.known_destinations[destination_hash]
entry[0] = time.time()
entry[1] = packet_hash
entry[2] = public_key
entry[3] = app_data
@staticmethod
def recall(target_hash, from_identity_hash=False):
def recall(target_hash, from_identity_hash=False, _no_use=False):
"""
Recall identity for a destination or identity hash. By default, this function
will return the identity associated with a given *destination* hash. As an
@@ -120,6 +128,7 @@ class Identity:
if from_identity_hash:
for destination_hash in Identity.known_destinations:
if target_hash == Identity.truncated_hash(Identity.known_destinations[destination_hash][2]):
if not _no_use: RNS.Reticulum.get_instance()._used_destination_data(destination_hash)
identity_data = Identity.known_destinations[destination_hash]
identity = Identity(create_keys=False)
identity.load_public_key(identity_data[2])
@@ -130,6 +139,7 @@ class Identity:
else:
if target_hash in Identity.known_destinations:
if not _no_use: RNS.Reticulum.get_instance()._used_destination_data(target_hash)
identity_data = Identity.known_destinations[target_hash]
identity = Identity(create_keys=False)
identity.load_public_key(identity_data[2])
@@ -146,7 +156,7 @@ class Identity:
return None
@staticmethod
def recall_app_data(destination_hash):
def recall_app_data(destination_hash, _no_use=False):
"""
Recall last heard app_data for a destination hash.
@@ -154,13 +164,14 @@ class Identity:
:returns: *Bytes* containing app_data, or *None* if the destination is unknown.
"""
if destination_hash in Identity.known_destinations:
if not _no_use: RNS.Reticulum.get_instance()._used_destination_data(destination_hash)
app_data = Identity.known_destinations[destination_hash][3]
return app_data
else:
return None
else: return None
@staticmethod
def save_known_destinations():
def save_known_destinations(background=False, recombine=True):
# TODO: Improve the storage method so we don't have to
# deserialize and serialize the entire table on every
# save, but the only changes. It might be possible to
@@ -181,34 +192,33 @@ class Identity:
Identity.saving_known_destinations = True
save_start = time.time()
storage_known_destinations = {}
if os.path.isfile(RNS.Reticulum.storagepath+"/known_destinations"):
if recombine:
storage_known_destinations = {}
if os.path.isfile(RNS.Reticulum.storagepath+"/known_destinations"):
try:
with open(RNS.Reticulum.storagepath+"/known_destinations","rb") as file:
storage_known_destinations = umsgpack.load(file)
except: pass
try:
with open(RNS.Reticulum.storagepath+"/known_destinations","rb") as file:
storage_known_destinations = umsgpack.load(file)
except:
pass
for destination_hash in storage_known_destinations:
if not destination_hash in Identity.known_destinations:
with Identity.known_destinations_lock:
Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash]
except Exception as e:
RNS.log("Skipped recombining known destinations from disk, since an error occurred: "+str(e), RNS.LOG_WARNING)
try:
for destination_hash in storage_known_destinations:
if not destination_hash in Identity.known_destinations:
Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash]
except Exception as e:
RNS.log("Skipped recombining known destinations from disk, since an error occurred: "+str(e), RNS.LOG_WARNING)
RNS.log("Saving "+str(len(Identity.known_destinations))+" known destinations to storage...", RNS.LOG_DEBUG)
RNS.log("Saving "+str(len(Identity.known_destinations))+" known destinations to storage...", RNS.LOG_VERBOSE)
with open(RNS.Reticulum.storagepath+"/known_destinations","wb") as file:
umsgpack.dump(Identity.known_destinations, file)
umsgpack.dump(Identity.known_destinations.copy(), file)
save_time = time.time() - save_start
if save_time < 1:
time_str = str(round(save_time*1000,2))+"ms"
else:
time_str = str(round(save_time,2))+"s"
if save_time < 1: time_str = str(round(save_time*1000,2))+"ms"
else: time_str = str(round(save_time,2))+"s"
RNS.log("Saved known destinations to storage in "+time_str, RNS.LOG_DEBUG)
RNS.log("Saved known destinations to storage in "+time_str, RNS.LOG_VERBOSE)
except Exception as e:
RNS.log("Error while saving known destinations to disk, the contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -219,6 +229,7 @@ class Identity:
@staticmethod
def load_known_destinations():
if os.path.isfile(RNS.Reticulum.storagepath+"/known_destinations"):
st = time.time()
try:
with open(RNS.Reticulum.storagepath+"/known_destinations","rb") as file:
loaded_known_destinations = umsgpack.load(file)
@@ -226,15 +237,102 @@ class Identity:
Identity.known_destinations = {}
for known_destination in loaded_known_destinations:
if len(known_destination) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8:
Identity.known_destinations[known_destination] = loaded_known_destinations[known_destination]
if len(loaded_known_destinations[known_destination]) < 5:
e = loaded_known_destinations[known_destination]
loaded_known_destinations[known_destination] = [e[0], e[1], e[2], e[3], 0]
RNS.log("Loaded "+str(len(Identity.known_destinations))+" known destination from storage", RNS.LOG_VERBOSE)
with Identity.known_destinations_lock:
Identity.known_destinations[known_destination] = loaded_known_destinations[known_destination]
RNS.log(f"Loaded {len(Identity.known_destinations)} known destination from storage in {RNS.prettyshorttime(time.time()-st)}", RNS.LOG_VERBOSE)
except Exception as e:
RNS.log("Error loading known destinations from disk, file will be recreated on exit", RNS.LOG_ERROR)
RNS.trace_exception(e)
else:
RNS.log("Destinations file does not exist, no known destinations loaded", RNS.LOG_VERBOSE)
@staticmethod
def _used_destination_data(destination_hash):
with Identity.known_destinations_lock:
if destination_hash in Identity.known_destinations:
if not Identity.known_destinations[destination_hash][4] < 0:
Identity.known_destinations[destination_hash][4] = time.time()
return True
return False
@staticmethod
def _retain_destination_data(destination_hash):
with Identity.known_destinations_lock:
if destination_hash in Identity.known_destinations:
Identity.known_destinations[destination_hash][4] = -1
return True
return False
@staticmethod
def _unretain_destination_data(destination_hash):
with Identity.known_destinations_lock:
if destination_hash in Identity.known_destinations:
Identity.known_destinations[destination_hash][4] = time.time()
return True
return False
@staticmethod
def clean_known_destinations():
now = time.time()
st = now
total = len(Identity.known_destinations)
stale = []
no_path = 0
retained = 0
never_used = 0
for destination_hash in Identity.known_destinations:
try:
if RNS.Transport.has_path(destination_hash): has_path = True
else:
has_path = False
no_path += 1
with Identity.known_destinations_lock:
if destination_hash in Identity.known_destinations:
last_announce = Identity.known_destinations[destination_hash][0]
last_use = 0
was_used = False
is_retained = False
if Identity.known_destinations[destination_hash][4] > 0:
was_used = True
last_use = Identity.known_destinations[destination_hash][4]
elif Identity.known_destinations[destination_hash][4] == 0:
was_used = False
never_used += 1
elif Identity.known_destinations[destination_hash][4] == -1:
is_retained = True
retained += 1
unused_for = time.time() - Identity.known_destinations[destination_hash][4]
if not is_retained and not has_path:
if not was_used and now - last_announce > RNS.Transport.UNUSED_DESTINATION_LINGER: stale.append(destination_hash)
elif unused_for > RNS.Transport.DESTINATION_TIMEOUT*1.25: stale.append(destination_hash)
except Exception as e: RNS.log(f"Faulty entry for {RNS.prettyhexrep(destination_hash)} while cleaning known destinations: {e}", RNS.LOG_DEBUG)
removed = 0
for destination_hash in stale:
with Identity.known_destinations_lock:
if destination_hash in Identity.known_destinations:
Identity.known_destinations.pop(destination_hash)
removed += 1
# RNS.log(f"Total destinations: {total}, stale: {len(stale)}, removed: {removed}, no path: {no_path}, never used: {never_used}, with path: {total-no_path}, used: {total-never_used}, retained: {retained}. Completed in {RNS.prettyshorttime(time.time()-st)}", RNS.LOG_WARNING) # TODO: Remove
if not RNS.Transport.owner.is_connected_to_shared_instance: Identity.save_known_destinations(recombine=False)
@staticmethod
def full_hash(data):
"""
@@ -493,9 +591,9 @@ class Identity:
return False
@staticmethod
def persist_data():
def persist_data(background=False):
if not RNS.Transport.owner.is_connected_to_shared_instance:
Identity.save_known_destinations()
Identity.save_known_destinations(background=background)
@staticmethod
def exit_handler():
+10
View File
@@ -530,6 +530,16 @@ class AutoInterface(Interface):
spawned_interface = AutoInterfacePeer(self, addr, ifname)
spawned_interface.OUT = self.OUT
spawned_interface.IN = self.IN
spawned_interface.ingress_control = self.ingress_control
spawned_interface.ic_max_held_announces = self.ic_max_held_announces
spawned_interface.ic_burst_hold = self.ic_burst_hold
spawned_interface.ic_burst_freq = self.ic_burst_freq
spawned_interface.ic_burst_freq_new = self.ic_burst_freq_new
spawned_interface.ic_new_time = self.ic_new_time
spawned_interface.ic_burst_penalty = self.ic_burst_penalty
spawned_interface.ic_held_release_interval = self.ic_held_release_interval
spawned_interface.parent_interface = self
spawned_interface.bitrate = self.bitrate
+19 -6
View File
@@ -228,10 +228,10 @@ class BackboneInterface(Interface):
if interface.socket:
fileno = interface.socket.fileno()
if fileno in BackboneInterface.spawned_interface_filenos:
try:
BackboneInterface.epoll.modify(interface.socket.fileno(), select.EPOLLOUT)
try: BackboneInterface.epoll.modify(fileno, select.EPOLLOUT)
except Exception as e:
RNS.trace_exception(e)
RNS.log(f"Error occurred on {interface} while modifying socket EPOLL state: {e}", RNS.LOG_WARNING)
raise e
@staticmethod
def __job():
@@ -270,8 +270,7 @@ class BackboneInterface(Interface):
spawned_interface.receive(received_bytes)
elif client_socket and fileno == client_socket.fileno() and (event & select.EPOLLOUT):
try:
written = client_socket.send(spawned_interface.transmit_buffer)
try: written = client_socket.send(spawned_interface.transmit_buffer)
except Exception as e:
written = 0
if not spawned_interface.detached: RNS.log(f"Error while writing to {spawned_interface}: {e}", RNS.LOG_DEBUG)
@@ -293,7 +292,11 @@ class BackboneInterface(Interface):
spawned_interface.receive(b"")
spawned_interface.transmit_buffer = spawned_interface.transmit_buffer[written:]
if len(spawned_interface.transmit_buffer) == 0: BackboneInterface.epoll.modify(fileno, select.EPOLLIN)
try:
if len(spawned_interface.transmit_buffer) == 0: BackboneInterface.epoll.modify(fileno, select.EPOLLIN)
except Exception as e:
RNS.log(f"Error while setting EPOLLIN on {spawned_interface}: {e}", RNS.LOG_ERROR)
spawned_interface.txb += written
if spawned_interface.parent_interface: spawned_interface.parent_interface.txb += written
@@ -344,6 +347,16 @@ class BackboneInterface(Interface):
spawned_interface = BackboneClientInterface(self.owner, spawned_configuration, connected_socket=socket)
spawned_interface.OUT = self.OUT
spawned_interface.IN = self.IN
spawned_interface.ingress_control = self.ingress_control
spawned_interface.ic_max_held_announces = self.ic_max_held_announces
spawned_interface.ic_burst_hold = self.ic_burst_hold
spawned_interface.ic_burst_freq = self.ic_burst_freq
spawned_interface.ic_burst_freq_new = self.ic_burst_freq_new
spawned_interface.ic_new_time = self.ic_new_time
spawned_interface.ic_burst_penalty = self.ic_burst_penalty
spawned_interface.ic_held_release_interval = self.ic_held_release_interval
spawned_interface.socket = socket
spawned_interface.target_ip = socket.getpeername()[0]
spawned_interface.target_port = str(socket.getpeername()[1])
+10
View File
@@ -948,6 +948,16 @@ class I2PInterface(Interface):
spawned_interface = I2PInterfacePeer(self, self.owner, interface_name, connected_socket=handler.request)
spawned_interface.OUT = True
spawned_interface.IN = True
spawned_interface.ingress_control = self.ingress_control
spawned_interface.ic_max_held_announces = self.ic_max_held_announces
spawned_interface.ic_burst_hold = self.ic_burst_hold
spawned_interface.ic_burst_freq = self.ic_burst_freq
spawned_interface.ic_burst_freq_new = self.ic_burst_freq_new
spawned_interface.ic_new_time = self.ic_new_time
spawned_interface.ic_burst_penalty = self.ic_burst_penalty
spawned_interface.ic_held_release_interval = self.ic_held_release_interval
spawned_interface.parent_interface = self
spawned_interface.online = True
spawned_interface.bitrate = self.bitrate
+27 -42
View File
@@ -55,8 +55,8 @@ class Interface:
# How many samples to use for announce
# frequency calculations
IA_FREQ_SAMPLES = 6
OA_FREQ_SAMPLES = 6
IA_FREQ_SAMPLES = 128
OA_FREQ_SAMPLES = 128
# Maximum amount of ingress limited announces
# to hold at any given time.
@@ -66,11 +66,12 @@ class Interface:
# considered to be newly created. Two
# hours by default.
IC_NEW_TIME = 2*60*60
IC_BURST_FREQ_NEW = 3.5
IC_BURST_FREQ = 12
IC_BURST_FREQ_NEW = 6
IC_BURST_FREQ = 35
IC_BURST_HOLD = 1*60
IC_BURST_PENALTY = 5*60
IC_HELD_RELEASE_INTERVAL = 30
IC_BURST_PENALTY = 15
IC_HELD_RELEASE_INTERVAL = 2
IC_DEQUE_MIN_SAMPLE = 32
AUTOCONFIGURE_MTU = False
FIXED_MTU = False
@@ -122,20 +123,19 @@ class Interface:
if self.ic_burst_active:
if ia_freq < freq_threshold and time.time() > self.ic_burst_activated+self.ic_burst_hold:
self.ic_burst_active = False
self.ic_held_release = time.time() + self.ic_burst_penalty
return True
else:
if ia_freq > freq_threshold:
self.ic_burst_active = True
self.ic_burst_activated = time.time()
self.ic_held_release = time.time() + self.ic_burst_penalty
return True
else:
return False
else: return False
else:
return False
else: return False
def optimise_mtu(self):
if self.AUTOCONFIGURE_MTU:
@@ -175,7 +175,7 @@ class Interface:
def process_held_announces(self):
try:
if not self.should_ingress_limit() and len(self.held_announces) > 0 and time.time() > self.ic_held_release:
if len(self.held_announces) > 0 and time.time() > self.ic_held_release:
freq_threshold = self.ic_burst_freq_new if self.age() < self.ic_new_time else self.ic_burst_freq
ia_freq = self.incoming_announce_frequency()
if ia_freq < freq_threshold:
@@ -191,8 +191,7 @@ class Interface:
RNS.log("Releasing held announce packet "+str(selected_announce_packet)+" from "+str(self), RNS.LOG_EXTREME)
self.ic_held_release = time.time() + self.ic_held_release_interval
self.held_announces.pop(selected_announce_packet.destination_hash)
def release():
RNS.Transport.inbound(selected_announce_packet.raw, selected_announce_packet.receiving_interface)
def release(): RNS.Transport.inbound(selected_announce_packet.raw, selected_announce_packet.receiving_interface)
threading.Thread(target=release, daemon=True).start()
except Exception as e:
@@ -210,38 +209,24 @@ class Interface:
self.parent_interface.sent_announce(from_spawned=True)
def incoming_announce_frequency(self):
if not len(self.ia_freq_deque) > 1:
return 0
n = len(self.ia_freq_deque)
if not n > self.IC_DEQUE_MIN_SAMPLE: return 0
else:
dq_len = len(self.ia_freq_deque)
delta_sum = 0
for i in range(1,dq_len):
delta_sum += self.ia_freq_deque[i]-self.ia_freq_deque[i-1]
delta_sum += time.time() - self.ia_freq_deque[dq_len-1]
if delta_sum == 0:
avg = 0
else:
avg = 1/(delta_sum/(dq_len))
return avg
oldest = self.ia_freq_deque[0]
span = time.time() - oldest
if span <= 0: return 0
hz = n / span
return hz
def outgoing_announce_frequency(self):
if not len(self.oa_freq_deque) > 1:
return 0
n = len(self.oa_freq_deque)
if not len(self.oa_freq_deque) > 1: return 0
else:
dq_len = len(self.oa_freq_deque)
delta_sum = 0
for i in range(1,dq_len):
delta_sum += self.oa_freq_deque[i]-self.oa_freq_deque[i-1]
delta_sum += time.time() - self.oa_freq_deque[dq_len-1]
if delta_sum == 0:
avg = 0
else:
avg = 1/(delta_sum/(dq_len))
return avg
oldest = self.oa_freq_deque[0]
span = time.time() - oldest
if span <= 0: return 0
hz = n / span
return hz
def process_announce_queue(self):
if not hasattr(self, "announce_cap"):
+2 -1
View File
@@ -328,7 +328,8 @@ class LocalClientInterface(Interface):
if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.clients -= 1
if hasattr(RNS.Transport, "owner") and RNS.Transport.owner != None:
RNS.Transport.owner._should_persist_data()
background = not self.detached
RNS.Transport.owner._should_persist_data(background=background)
if nowarning == False:
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
+10
View File
@@ -579,6 +579,16 @@ class TCPServerInterface(Interface):
spawned_interface = TCPClientInterface(self.owner, spawned_configuration, connected_socket=handler.request)
spawned_interface.OUT = self.OUT
spawned_interface.IN = self.IN
spawned_interface.ingress_control = self.ingress_control
spawned_interface.ic_max_held_announces = self.ic_max_held_announces
spawned_interface.ic_burst_hold = self.ic_burst_hold
spawned_interface.ic_burst_freq = self.ic_burst_freq
spawned_interface.ic_burst_freq_new = self.ic_burst_freq_new
spawned_interface.ic_new_time = self.ic_new_time
spawned_interface.ic_burst_penalty = self.ic_burst_penalty
spawned_interface.ic_held_release_interval = self.ic_held_release_interval
spawned_interface.target_ip = handler.client_address[0]
spawned_interface.target_port = str(handler.client_address[1])
spawned_interface.parent_interface = self
+10
View File
@@ -942,6 +942,16 @@ class WeaveInterface(Interface):
spawned_interface = WeaveInterfacePeer(self, endpoint_addr)
spawned_interface.OUT = self.OUT
spawned_interface.IN = self.IN
spawned_interface.ingress_control = self.ingress_control
spawned_interface.ic_max_held_announces = self.ic_max_held_announces
spawned_interface.ic_burst_hold = self.ic_burst_hold
spawned_interface.ic_burst_freq = self.ic_burst_freq
spawned_interface.ic_burst_freq_new = self.ic_burst_freq_new
spawned_interface.ic_new_time = self.ic_new_time
spawned_interface.ic_burst_penalty = self.ic_burst_penalty
spawned_interface.ic_held_release_interval = self.ic_held_release_interval
spawned_interface.parent_interface = self
spawned_interface.bitrate = self.bitrate
+15 -29
View File
@@ -722,12 +722,9 @@ class Link:
pass
def link_closed(self):
for resource in self.incoming_resources:
resource.cancel()
for resource in self.outgoing_resources:
resource.cancel()
if self._channel:
self._channel._shutdown()
for resource in self.incoming_resources: resource.cancel()
for resource in self.outgoing_resources: resource.cancel()
if self._channel: self._channel._shutdown()
self.prv = None
self.pub = None
@@ -741,8 +738,7 @@ class Link:
self.destination.links.remove(self)
if self.callbacks.link_closed != None:
try:
self.callbacks.link_closed(self)
try: self.callbacks.link_closed(self)
except Exception as e:
RNS.log("Error while executing link closed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -1181,7 +1177,7 @@ class Link:
resource_hash = packet.data[0:RNS.Identity.HASHLENGTH//8]
for resource in self.outgoing_resources:
if resource_hash == resource.hash:
def job(): resource.validate_proof(packet.data)
def job(resource=resource): resource.validate_proof(packet.data)
threading.Thread(target=job, daemon=True).start()
self.__update_phy_stats(packet, query_shared=True)
@@ -1300,10 +1296,8 @@ class Link:
:param resource_strategy: One of ``RNS.Link.ACCEPT_NONE``, ``RNS.Link.ACCEPT_ALL`` or ``RNS.Link.ACCEPT_APP``. If ``RNS.Link.ACCEPT_APP`` is set, the `resource_callback` will be called to determine whether the resource should be accepted or not.
:raises: *TypeError* if the resource strategy is unsupported.
"""
if not resource_strategy in Link.resource_strategies:
raise TypeError("Unsupported resource strategy")
else:
self.resource_strategy = resource_strategy
if not resource_strategy in Link.resource_strategies: raise TypeError("Unsupported resource strategy")
else: self.resource_strategy = resource_strategy
def register_outgoing_resource(self, resource):
self.outgoing_resources.append(resource)
@@ -1313,8 +1307,7 @@ class Link:
def has_incoming_resource(self, resource):
for incoming_resource in self.incoming_resources:
if incoming_resource.hash == resource.hash:
return True
if incoming_resource.hash == resource.hash: return True
return False
@@ -1325,25 +1318,18 @@ class Link:
return self.last_resource_eifr
def cancel_outgoing_resource(self, resource):
if resource in self.outgoing_resources:
self.outgoing_resources.remove(resource)
else:
RNS.log("Attempt to cancel a non-existing outgoing resource", RNS.LOG_ERROR)
if resource in self.outgoing_resources: self.outgoing_resources.remove(resource)
else: RNS.log("Attempt to cancel a non-existing outgoing resource", RNS.LOG_ERROR)
def cancel_incoming_resource(self, resource):
if resource in self.incoming_resources:
self.incoming_resources.remove(resource)
else:
RNS.log("Attempt to cancel a non-existing incoming resource", RNS.LOG_ERROR)
if resource in self.incoming_resources: self.incoming_resources.remove(resource)
else: RNS.log("Attempt to cancel a non-existing incoming resource", RNS.LOG_ERROR)
def ready_for_new_resource(self):
if len(self.outgoing_resources) > 0:
return False
else:
return True
if len(self.outgoing_resources) > 0: return False
else: return True
def __str__(self):
return RNS.prettyhexrep(self.link_id)
def __str__(self): return RNS.prettyhexrep(self.link_id)
class RequestReceipt():
+2 -2
View File
@@ -293,7 +293,7 @@ class Packet:
if RNS.Transport.outbound(self): return self.receipt
else:
RNS.log("No interfaces could process the outbound packet", RNS.LOG_ERROR)
RNS.log("No interfaces could process the outbound packet", RNS.LOG_DEBUG)
self.sent = False
self.receipt = None
return False
@@ -315,7 +315,7 @@ class Packet:
if RNS.Transport.outbound(self):
return self.receipt
else:
RNS.log("No interfaces could process the outbound packet", RNS.LOG_ERROR)
RNS.log("Re-send failed. No interfaces could process the outbound packet", RNS.LOG_WARNING)
self.sent = False
self.receipt = None
return False
+6 -2
View File
@@ -126,6 +126,7 @@ class Resource:
PART_TIMEOUT_FACTOR = 4
PART_TIMEOUT_FACTOR_AFTER_RTT = 2
PROOF_TIMEOUT_FACTOR = 3
HMU_WAIT_FACTOR = 3.5
MAX_RETRIES = 16
MAX_ADV_RETRIES = 4
SENDER_GRACE_TIME = 10.0
@@ -594,15 +595,16 @@ class Resource:
extra_wait = retries_used * Resource.PER_RETRY_DELAY
self.update_eifr()
expected_hmu_wait_remaining = (self.sdu*8*self.HMU_WAIT_FACTOR)/self.eifr if self.waiting_for_hmu or self.outstanding_parts == 0 else 0
expected_tof_remaining = (self.outstanding_parts*self.sdu*8)/self.eifr
if self.req_resp_rtt_rate != 0:
sleep_time = self.last_activity + self.part_timeout_factor*expected_tof_remaining + Resource.RETRY_GRACE_TIME + extra_wait - time.time()
sleep_time = self.last_activity + self.part_timeout_factor*expected_tof_remaining + expected_hmu_wait_remaining + Resource.RETRY_GRACE_TIME + extra_wait - time.time()
else:
sleep_time = self.last_activity + self.part_timeout_factor*((3*self.sdu)/self.eifr) + Resource.RETRY_GRACE_TIME + extra_wait - time.time()
# TODO: Remove debug at some point
# RNS.log(f"EIFR {RNS.prettyspeed(self.eifr)}, ETOF {RNS.prettyshorttime(expected_tof_remaining)} ", RNS.LOG_DEBUG, pt=True)
# RNS.log(f"EIFR {RNS.prettyspeed(self.eifr)}, ETOF {RNS.prettyshorttime(expected_tof_remaining)}, EHWR {RNS.prettyshorttime(expected_hmu_wait_remaining)}", RNS.LOG_DEBUG, pt=True)
# RNS.log(f"Resource ST {RNS.prettyshorttime(sleep_time)}, RTT {RNS.prettyshorttime(self.rtt or self.link.rtt)}, {self.outstanding_parts} left", RNS.LOG_DEBUG, pt=True)
if sleep_time < 0:
@@ -959,6 +961,7 @@ class Resource:
self.last_activity = time.time()
self.req_sent = self.last_activity
self.req_sent_bytes = len(request_packet.raw)
self.rtt_rxd_bytes_at_part_req = self.rtt_rxd_bytes
self.req_resp = None
except Exception as e:
@@ -1065,6 +1068,7 @@ class Resource:
"""
Cancels transferring the resource.
"""
if self.next_segment: self.next_segment.cancel()
if self.status < Resource.COMPLETE:
self.status = Resource.FAILED
if self.initiator:
+55 -14
View File
@@ -47,6 +47,7 @@ else:
from RNS.Interfaces import *
from RNS.vendor.configobj import ConfigObj
from threading import Lock
import configparser
import multiprocessing.connection
import importlib.util
@@ -171,6 +172,8 @@ class Reticulum:
cachepath = ""
interfacepath = ""
gracious_persist_lock = Lock()
__instance = None
__interface_detach_ran = False
@@ -361,11 +364,11 @@ class Reticulum:
now = time.time()
if now > self.last_cache_clean+Reticulum.CLEAN_INTERVAL:
self.__clean_caches()
self.__clean_caches(background=True)
self.last_cache_clean = time.time()
if now > self.last_data_persist+Reticulum.PERSIST_INTERVAL:
self.__persist_data()
self.__persist_data(background=True)
time.sleep(Reticulum.JOB_INTERVAL)
@@ -960,7 +963,7 @@ class Reticulum:
interface.optimise_mtu()
if ifac_size != None: interface.ifac_size = ifac_size
else: interface.ifac_size = 8
else: interface.ifac_size = interface.DEFAULT_IFAC_SIZE
interface.announce_cap = announce_cap if announce_cap != None else Reticulum.ANNOUNCE_CAP/100.0
interface.announce_rate_target = announce_rate_target
@@ -993,16 +996,20 @@ class Reticulum:
RNS.Transport.interfaces.append(interface)
interface.final_init()
def _should_persist_data(self):
def _should_persist_data(self, background=False):
if time.time() > self.last_data_persist+Reticulum.GRACIOUS_PERSIST_INTERVAL:
self.__persist_data()
def job(): self.__persist_data(background=background)
if background: threading.Thread(target=job, daemon=True).start()
else: job()
def __persist_data(self):
RNS.Transport.persist_data()
RNS.Identity.persist_data()
self.last_data_persist = time.time()
def __persist_data(self, background=False):
if Reticulum.gracious_persist_lock.locked(): return
with Reticulum.gracious_persist_lock:
RNS.Transport.persist_data(background=background)
RNS.Identity.persist_data(background=background)
self.last_data_persist = time.time()
def __clean_caches(self):
def __clean_caches(self, background=False):
RNS.log("Cleaning resource and packet caches...", RNS.LOG_EXTREME)
now = time.time()
@@ -1013,8 +1020,8 @@ class Reticulum:
filepath = self.resourcepath + "/" + filename
mtime = os.path.getmtime(filepath)
age = now - mtime
if age > Reticulum.RESOURCE_CACHE:
os.unlink(filepath)
if age > Reticulum.RESOURCE_CACHE: os.unlink(filepath)
if background: time.sleep(0.001)
except Exception as e:
RNS.log("Error while cleaning resources cache, the contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -1026,8 +1033,8 @@ class Reticulum:
filepath = self.cachepath + "/" + filename
mtime = os.path.getmtime(filepath)
age = now - mtime
if age > RNS.Transport.DESTINATION_TIMEOUT:
os.unlink(filepath)
if age > RNS.Transport.DESTINATION_TIMEOUT: os.unlink(filepath)
if background: time.sleep(0.001)
except Exception as e:
RNS.log("Error while cleaning resources cache, the contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -1080,6 +1087,13 @@ class Reticulum:
identity_hash = call["unblackhole_identity"]
rpc_connection.send(self.unblackhole_identity(identity_hash))
if "destination_data" in call:
operation = call["destination_data"]
destination_hash = call["destination_hash"]
if operation == "used": rpc_connection.send(self._used_destination_data(destination_hash))
elif operation == "retain": rpc_connection.send(self._retain_destination_data(destination_hash))
elif operation == "unretain": rpc_connection.send(self._unretain_destination_data(destination_hash))
rpc_connection.close()
except Exception as e:
@@ -1087,6 +1101,33 @@ class Reticulum:
def get_rpc_client(self): return multiprocessing.connection.Client(self.rpc_addr, family=self.rpc_type, authkey=self.rpc_key)
def _used_destination_data(self, destination_hash):
if self.is_connected_to_shared_instance:
rpc_connection = self.get_rpc_client()
rpc_connection.send({"destination_data": "used", "destination_hash": destination_hash})
response = rpc_connection.recv()
return response
else: return RNS.Identity._used_destination_data(destination_hash)
def _retain_destination_data(self, destination_hash):
if self.is_connected_to_shared_instance:
rpc_connection = self.get_rpc_client()
rpc_connection.send({"destination_data": "retain", "destination_hash": destination_hash})
response = rpc_connection.recv()
return response
else: return RNS.Identity._retain_destination_data(destination_hash)
def _unretain_destination_data(self, destination_hash):
if self.is_connected_to_shared_instance:
rpc_connection = self.get_rpc_client()
rpc_connection.send({"destination_data": "unretain", "destination_hash": destination_hash})
response = rpc_connection.recv()
return response
else: return RNS.Identity._unretain_destination_data(destination_hash)
def get_interface_stats(self):
if self.is_connected_to_shared_instance:
rpc_connection = self.get_rpc_client()
+914 -770
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -240,7 +240,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if "cr" in i: print(f"Coding Rate : {i['cr']}")
if "modulation" in i: print(f"Modulation : {i['modulation']}")
if "reachable_on" in i: print(f"Address : {i['reachable_on']}")
if "reachable_on" in i: print(f"Port : {i['port']}")
if "port" in i: print(f"Port : {i['port']}")
print(f"Stamp Value : {i['value']}")
+1
View File
@@ -225,6 +225,7 @@ def prettysize(num, suffix='B'):
return "%.2f%s%s" % (num, last_unit, suffix)
def prettyfrequency(hz, suffix="Hz"):
if hz == 0: return "0 Hz"
num = hz*1e6
units = ["µ", "m", "", "K","M","G","T","P","E","Z"]
last_unit = "Y"
+1 -1
View File
@@ -1 +1 @@
__version__ = "1.1.4"
__version__ = "1.1.8"
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
config: c08506be96edc5850ef3daad6fd8b69c
config: 8e121b74ca3e570ddba7366f5d83f982
tags: 645f666f9bcd5a90fca523b33c5a78b7
+2 -2
View File
@@ -125,7 +125,7 @@ A `Reticulum MeshChat fork from the future <https://git.quad4.io/RNS-Things/Mesh
:target: https://git.quad4.io/RNS-Things/MeshChatX
Features include full LXST support, custom voicemail, phonebook, contact sharing, and ringtone support, multi-identity handling, modern UI/UX, offline documentation, expanded tools, page archiving, integrated maps and improved application security.
Features include full LXST support, custom voicemail, phonebook, contact sharing, and ringtone support, multi-identity handling, modern UI/UX, offline documentation, expanded tools, page archiving, integrated maps, telemetry and improved application security.
.. raw:: latex
@@ -216,7 +216,7 @@ RetiBBS allows users to communicate through message boards in a secure manner.
RBrowser
^^^^^^^^
The `rBrowser <https://github.com/fr33n0w/rBrowser>`_ program is a cross-platoform, standalone, web-based browser for exploring NomadNetwork Nodes over Reticulum Network. It automatically discovers NomadNet nodes through network announces and provides a user-friendly interface for browsing distributed content with Micron markup support.
The `rBrowser <https://github.com/fr33n0w/rBrowser>`_ program is a cross-platform, standalone, web-based browser for exploring NomadNetwork Nodes over Reticulum Network. It automatically discovers NomadNet nodes through network announces and provides a user-friendly interface for browsing distributed content with Micron markup support.
.. only:: html
+1 -1
View File
@@ -1,5 +1,5 @@
const DOCUMENTATION_OPTIONS = {
VERSION: '1.1.4',
VERSION: '1.1.8',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Code Examples - Reticulum Network Stack 1.1.4 documentation</title>
<title>Code Examples - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -3663,7 +3663,7 @@ will be fully on-par with natively included interfaces, including all supported
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 1.1.4 documentation</title>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -294,7 +294,7 @@
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#"><link rel="search" title="Search" href="search.html">
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 --><title>Index - Reticulum Network Stack 1.1.4 documentation</title>
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 --><title>Index - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -178,7 +178,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -202,7 +202,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -836,7 +836,7 @@
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Getting Started Fast - Reticulum Network Stack 1.1.4 documentation</title>
<title>Getting Started Fast - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -966,7 +966,7 @@ All other available modules will still be loaded when needed.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Communications Hardware - Reticulum Network Stack 1.1.4 documentation</title>
<title>Communications Hardware - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -674,7 +674,7 @@ can be used with Reticulum. This includes virtual software modems such as
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Reticulum Network Stack 1.1.4 documentation</title>
<title>Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="#"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -631,7 +631,7 @@ to participate in the development of Reticulum itself.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Configuring Interfaces - Reticulum Network Stack 1.1.4 documentation</title>
<title>Configuring Interfaces - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -1684,7 +1684,7 @@ to <code class="docutils literal notranslate"><span class="pre">30</span></code>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Reticulum License - Reticulum Network Stack 1.1.4 documentation</title>
<title>Reticulum License - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -343,7 +343,7 @@ SOFTWARE.
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Building Networks - Reticulum Network Stack 1.1.4 documentation</title>
<title>Building Networks - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -662,7 +662,7 @@ differently than a mobile device roaming between radio cells.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
Binary file not shown.
+6 -6
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>API Reference - Reticulum Network Stack 1.1.4 documentation</title>
<title>API Reference - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -506,7 +506,7 @@ for addressable hashes and other purposes. Non-configurable.</p>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Identity.recall">
<em class="property"><span class="k"><span class="pre">static</span></span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">recall</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">target_hash</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">from_identity_hash</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.recall" title="Link to this definition"></a></dt>
<em class="property"><span class="k"><span class="pre">static</span></span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">recall</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">target_hash</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">from_identity_hash</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">_no_use</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.recall" title="Link to this definition"></a></dt>
<dd><p>Recall identity for a destination or identity hash. By default, this function
will return the identity associated with a given <em>destination</em> hash. As an
example, if you know the <code class="docutils literal notranslate"><span class="pre">lxmf.delivery</span></code> destination hash of an endpoint,
@@ -528,7 +528,7 @@ search for an identity from a known <em>identity hash</em>, by setting the
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Identity.recall_app_data">
<em class="property"><span class="k"><span class="pre">static</span></span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">recall_app_data</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination_hash</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.recall_app_data" title="Link to this definition"></a></dt>
<em class="property"><span class="k"><span class="pre">static</span></span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">recall_app_data</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">destination_hash</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">_no_use</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Identity.recall_app_data" title="Link to this definition"></a></dt>
<dd><p>Recall last heard app_data for a destination hash.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
@@ -2472,7 +2472,7 @@ will announce it.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -8,7 +8,7 @@
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<meta name="robots" content="noindex" />
<title>Search - Reticulum Network Stack 1.1.4 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<title>Search - Reticulum Network Stack 1.1.8 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -302,7 +302,7 @@
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Programs Using Reticulum - Reticulum Network Stack 1.1.4 documentation</title>
<title>Programs Using Reticulum - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -330,7 +330,7 @@ plugin system for expandability.</p>
<p>A <a class="reference external" href="https://git.quad4.io/RNS-Things/MeshChatX">Reticulum MeshChat fork from the future</a>, with the goal of providing everything you need for Reticulum, LXMF, and LXST in one beautiful and feature-rich application. This project is separate from the original Reticulum MeshChat project, and is not affiliated with the original project.</p>
<a class="reference external image-reference" href="https://git.quad4.io/RNS-Things/MeshChatX"><img alt="_images/meshchatx.webp" class="align-center" src="_images/meshchatx.webp" />
</a>
<p>Features include full LXST support, custom voicemail, phonebook, contact sharing, and ringtone support, multi-identity handling, modern UI/UX, offline documentation, expanded tools, page archiving, integrated maps and improved application security.</p>
<p>Features include full LXST support, custom voicemail, phonebook, contact sharing, and ringtone support, multi-identity handling, modern UI/UX, offline documentation, expanded tools, page archiving, integrated maps, telemetry and improved application security.</p>
</section>
<section id="meshchat">
<h3>MeshChat<a class="headerlink" href="#meshchat" title="Link to this heading"></a></h3>
@@ -367,7 +367,7 @@ using LXMF.</p>
</section>
<section id="rbrowser">
<h3>RBrowser<a class="headerlink" href="#rbrowser" title="Link to this heading"></a></h3>
<p>The <a class="reference external" href="https://github.com/fr33n0w/rBrowser">rBrowser</a> program is a cross-platoform, standalone, web-based browser for exploring NomadNetwork Nodes over Reticulum Network. It automatically discovers NomadNet nodes through network announces and provides a user-friendly interface for browsing distributed content with Micron markup support.</p>
<p>The <a class="reference external" href="https://github.com/fr33n0w/rBrowser">rBrowser</a> program is a cross-platform, standalone, web-based browser for exploring NomadNetwork Nodes over Reticulum Network. It automatically discovers NomadNet nodes through network announces and provides a user-friendly interface for browsing distributed content with Micron markup support.</p>
<a class="reference external image-reference" href="https://github.com/fr33n0w/rBrowser"><img alt="_images/rbrowser.webp" class="align-center" src="_images/rbrowser.webp" />
</a>
<p>Includes useful features like automatic listening for announce, adding nodes to favorites, browsing and rendering any kind of NomadNet links, downloading files from remote nodes, a unique local NomadNet Search Engine and more.</p>
@@ -533,7 +533,7 @@ using LXMF.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Support Reticulum - Reticulum Network Stack 1.1.4 documentation</title>
<title>Support Reticulum - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -381,7 +381,7 @@ circumstances, so we rely on old-fashioned human feedback.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Understanding Reticulum - Reticulum Network Stack 1.1.4 documentation</title>
<title>Understanding Reticulum - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -1336,7 +1336,7 @@ those risks are acceptable to you.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Using Reticulum on Your System - Reticulum Network Stack 1.1.4 documentation</title>
<title>Using Reticulum on Your System - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -1395,7 +1395,7 @@ systemctl --user enable rnsd.service
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>What is Reticulum? - Reticulum Network Stack 1.1.4 documentation</title>
<title>What is Reticulum? - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -503,7 +503,7 @@ network, and vice versa.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+4 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Zen of Reticulum - Reticulum Network Stack 1.1.4 documentation</title>
<title>Zen of Reticulum - Reticulum Network Stack 1.1.8 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.4 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.1.8 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.4 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.1.8 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -675,7 +675,7 @@ Imagine a messaging app. You write it once. It works on a laptop connected to fi
</aside>
</div>
</div><script src="_static/documentation_options.js?v=00f267c6"></script>
</div><script src="_static/documentation_options.js?v=df104e6e"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+2 -2
View File
@@ -125,7 +125,7 @@ A `Reticulum MeshChat fork from the future <https://git.quad4.io/RNS-Things/Mesh
:target: https://git.quad4.io/RNS-Things/MeshChatX
Features include full LXST support, custom voicemail, phonebook, contact sharing, and ringtone support, multi-identity handling, modern UI/UX, offline documentation, expanded tools, page archiving, integrated maps and improved application security.
Features include full LXST support, custom voicemail, phonebook, contact sharing, and ringtone support, multi-identity handling, modern UI/UX, offline documentation, expanded tools, page archiving, integrated maps, telemetry and improved application security.
.. raw:: latex
@@ -216,7 +216,7 @@ RetiBBS allows users to communicate through message boards in a secure manner.
RBrowser
^^^^^^^^
The `rBrowser <https://github.com/fr33n0w/rBrowser>`_ program is a cross-platoform, standalone, web-based browser for exploring NomadNetwork Nodes over Reticulum Network. It automatically discovers NomadNet nodes through network announces and provides a user-friendly interface for browsing distributed content with Micron markup support.
The `rBrowser <https://github.com/fr33n0w/rBrowser>`_ program is a cross-platform, standalone, web-based browser for exploring NomadNetwork Nodes over Reticulum Network. It automatically discovers NomadNet nodes through network announces and provides a user-friendly interface for browsing distributed content with Micron markup support.
.. only:: html
+1 -1
View File
@@ -768,7 +768,7 @@ class TestLink(unittest.TestCase):
data = bytearray()
for rx in received:
data.extend(rx)
if rx: data.extend(rx)
rx_message = data
print(f"Received {len(received)} chunks, totalling {len(rx_message)} bytes")