Compare commits

...

9 Commits

Author SHA1 Message Date
markqvist
eafe77718f Merge pull request #85 from RFnexus/master
Strip whitespace from LXMF address and URL dialog
2025-12-13 13:56:50 +01:00
Zenith
6d2bf21f0d Strip whitespace from LXMF address and URL dialog 2025-12-12 19:21:03 -05:00
Mark Qvist
eac9021c75 Updated required LXMF to 0.9.1 2025-11-03 22:31:34 +01:00
Mark Qvist
e6688b157e Ensure static_peers is initialized regardless of config 2025-11-03 12:38:07 +01:00
Mark Qvist
ad2cefa329 Allow configuring propagation node stamp cost 2025-11-01 18:27:47 +01:00
Mark Qvist
373315423e Updated config file descriptions. Ensure propagation node is disabled by default. 2025-11-01 17:49:39 +01:00
Mark Qvist
025cae6ebf Updated version and dependencies 2025-10-30 14:12:46 +01:00
Mark Qvist
0baebe5a3c Propagation node stamp cost handling 2025-10-30 14:09:12 +01:00
Mark Qvist
3fedd0af30 Added separate LXMF PN message size and sync transfer size limits 2025-10-29 23:19:33 +01:00
7 changed files with 117 additions and 28 deletions

View File

@@ -6,6 +6,8 @@ import nomadnet
import threading import threading
import RNS.vendor.umsgpack as msgpack import RNS.vendor.umsgpack as msgpack
from LXMF import pn_announce_data_is_valid
class PNAnnounceHandler: class PNAnnounceHandler:
def __init__(self, owner): def __init__(self, owner):
self.aspect_filter = "lxmf.propagation" self.aspect_filter = "lxmf.propagation"
@@ -13,10 +15,10 @@ class PNAnnounceHandler:
def received_announce(self, destination_hash, announced_identity, app_data): def received_announce(self, destination_hash, announced_identity, app_data):
try: try:
if type(app_data) == bytes: if pn_announce_data_is_valid(app_data):
data = msgpack.unpackb(app_data) data = msgpack.unpackb(app_data)
if data[0] == True: if data[2] == True:
RNS.log("Received active propagation node announce from "+RNS.prettyhexrep(destination_hash)) RNS.log("Received active propagation node announce from "+RNS.prettyhexrep(destination_hash))
associated_peer = RNS.Destination.hash_from_name_and_identity("lxmf.delivery", announced_identity) associated_peer = RNS.Destination.hash_from_name_and_identity("lxmf.delivery", announced_identity)
@@ -193,7 +195,8 @@ class Directory:
found_node = True found_node = True
break break
if not found_node: # TODO: Remove debug and rethink this (needs way to set PN when node is saved)
if True or not found_node:
if self.app.compact_stream: if self.app.compact_stream:
try: try:
remove_announces = [] remove_announces = []

View File

@@ -122,13 +122,16 @@ class NomadNetworkApp:
self.page_refresh_interval = 0 self.page_refresh_interval = 0
self.file_refresh_interval = 0 self.file_refresh_interval = 0
self.static_peers = []
self.peer_announce_at_start = True self.peer_announce_at_start = True
self.try_propagation_on_fail = True self.try_propagation_on_fail = True
self.disable_propagation = False self.disable_propagation = True
self.notify_on_new_message = True self.notify_on_new_message = True
self.lxmf_max_propagation_size = None self.lxmf_max_propagation_size = None
self.lxmf_max_sync_size = None
self.lxmf_max_incoming_size = None self.lxmf_max_incoming_size = None
self.node_propagation_cost = LXMF.LXMRouter.PROPAGATION_COST
self.periodic_lxmf_sync = True self.periodic_lxmf_sync = True
self.lxmf_sync_interval = 360*60 self.lxmf_sync_interval = 360*60
@@ -302,8 +305,8 @@ class NomadNetworkApp:
self.message_router = LXMF.LXMRouter( self.message_router = LXMF.LXMRouter(
identity = self.identity, storagepath = self.storagepath, autopeer = True, identity = self.identity, storagepath = self.storagepath, autopeer = True,
propagation_limit = self.lxmf_max_propagation_size, delivery_limit = self.lxmf_max_incoming_size, propagation_limit = self.lxmf_max_propagation_size, sync_limit = self.lxmf_max_sync_size, delivery_limit = self.lxmf_max_incoming_size,
max_peers = self.max_peers, static_peers = static_peers, max_peers = self.max_peers, static_peers = static_peers, propagation_cost=self.node_propagation_cost
) )
self.message_router.register_delivery_callback(self.lxmf_delivery) self.message_router.register_delivery_callback(self.lxmf_delivery)
@@ -876,7 +879,7 @@ class NomadNetworkApp:
self.node_name = self.config["node"]["node_name"] self.node_name = self.config["node"]["node_name"]
if not "disable_propagation" in self.config["node"]: if not "disable_propagation" in self.config["node"]:
self.disable_propagation = False self.disable_propagation = True
else: else:
self.disable_propagation = self.config["node"].as_bool("disable_propagation") self.disable_propagation = self.config["node"].as_bool("disable_propagation")
@@ -888,6 +891,14 @@ class NomadNetworkApp:
value = 1 value = 1
self.lxmf_max_propagation_size = value self.lxmf_max_propagation_size = value
if not "max_sync_size" in self.config["node"]:
self.lxmf_max_sync_size = 256*40
else:
value = self.config["node"].as_float("max_sync_size")
if value < self.lxmf_max_propagation_size:
value = self.lxmf_max_propagation_size
self.lxmf_max_sync_size = value
if not "announce_at_start" in self.config["node"]: if not "announce_at_start" in self.config["node"]:
self.node_announce_at_start = False self.node_announce_at_start = False
else: else:
@@ -901,6 +912,13 @@ class NomadNetworkApp:
if value < 1: if value < 1:
value = 1 value = 1
self.node_announce_interval = value self.node_announce_interval = value
if not "propagation_cost" in self.config["node"]:
self.node_propagation_cost = 16
else:
value = self.config["node"].as_int("propagation_cost")
if value < 13: value = 13
self.node_propagation_cost = value
if "pages_path" in self.config["node"]: if "pages_path" in self.config["node"]:
self.pagespath = self.config["node"]["pages_path"] self.pagespath = self.config["node"]["pages_path"]
@@ -1162,14 +1180,55 @@ announce_at_start = Yes
# When Nomad Network is hosting a page-serving # When Nomad Network is hosting a page-serving
# node, it can also act as an LXMF propagation # node, it can also act as an LXMF propagation
# node. If there is already a large amount of # node. This is a convenient feature that lets
# you easily set up and run a propagation node
# on the network, but it is not as fully
# featured as using the lxmd program to host a
# propagation node. For complete control and
# flexibility, use lxmd to run a PN. For a
# small local system or network, the built-in
# PN functionality will suffice for most cases.
#
# If there is already a large amount of
# propagation nodes on the network, or you # propagation nodes on the network, or you
# simply want to run a pageserving-only node, # simply want to run a pageserving-only node,
# you can disable running a propagation node. # you should disable running a propagation node.
# Due to lots of propagation nodes being # Due to lots of propagation nodes being
# available, this is currently the default. # available, this is currently the default.
disable_propagation = Yes disable_propagation = Yes
# For clients and other propagation nodes
# delivering messages via this node, you can
# configure the minimum required propagation
# stamp costs. All messages delivered to the
# propagation node network must have a valid
# propagation stamp, or they will be rejected.
# Clients automatically detect the stamp cost
# for the node they are delivering to, and
# compute a corresponding stamp before trying
# to deliver the message to the propagation
# node.
#
# Propagation stamps are easier to verify in
# large batches, and therefore also somewhat
# easier to compute for the senders. As such,
# a reasonable propagation stamp cost should
# be a bit higher than the normal peer-to-peer
# stamp costs.
#
# Propagation stamps does not incur any extra
# load for propagation nodes processing them,
# since they are only required to verify that
# they are correct, and only the generation
# is computationally costly. Setting a sensible
# propagation stamp cost (and periodically
# checking the average network consensus) helps
# keep spam and misuse out of the propagation
# node network.
propagation_cost = 16
# The maximum amount of storage to use for # The maximum amount of storage to use for
# the LXMF Propagation Node message store, # the LXMF Propagation Node message store,
# specified in megabytes. When this limit # specified in megabytes. When this limit
@@ -1179,19 +1238,26 @@ disable_propagation = Yes
# new and small. Large and old messages will # new and small. Large and old messages will
# be removed first. This setting is optional # be removed first. This setting is optional
# and defaults to 2 gigabytes. # and defaults to 2 gigabytes.
# message_storage_limit = 2000 # message_storage_limit = 2000
# The maximum accepted transfer size per in- # The maximum accepted transfer size per in-
# coming propagation transfer, in kilobytes. # coming propagation message, in kilobytes.
# This also sets the upper limit for the size # This sets the upper limit for the size of
# of single messages accepted onto this node. # single messages accepted onto this node.
max_transfer_size = 256
# The maximum accepted transfer size per in-
# coming propagation node sync.
# #
# If a node wants to propagate a larger number # If a node wants to propagate a larger number
# of messages to this node, than what can fit # of messages to this node, than what can fit
# within this limit, it will prioritise sending # within this limit, it will prioritise sending
# the smallest, newest messages first, and try # the smallest messages first, and try again
# with any remaining messages at a later point. # with any remaining messages at a later point.
max_transfer_size = 256
max_sync_size = 10240
# You can tell the LXMF message router to # You can tell the LXMF message router to
# prioritise storage for one or more # prioritise storage for one or more
@@ -1200,29 +1266,34 @@ max_transfer_size = 256
# keeping messages for destinations specified # keeping messages for destinations specified
# with this option. This setting is optional, # with this option. This setting is optional,
# and generally you do not need to use it. # and generally you do not need to use it.
# prioritise_destinations = 41d20c727598a3fbbdf9106133a3a0ed, d924b81822ca24e68e2effea99bcb8cf # prioritise_destinations = 41d20c727598a3fbbdf9106133a3a0ed, d924b81822ca24e68e2effea99bcb8cf
# You can configure the maximum number of other # You can configure the maximum number of other
# propagation nodes that this node will peer # propagation nodes that this node will peer
# with automatically. The default is 50. # with automatically. The default is 20.
# max_peers = 25
# max_peers = 20
# You can configure a list of static propagation # You can configure a list of static propagation
# node peers, that this node will always be # node peers, that this node will always be
# peered with, by specifying a list of # peered with, by specifying a list of
# destination hashes. # destination hashes.
# static_peers = e17f833c4ddf8890dd3a79a6fea8161d, 5a2d0029b6e5ec87020abaea0d746da4 # static_peers = e17f833c4ddf8890dd3a79a6fea8161d, 5a2d0029b6e5ec87020abaea0d746da4
# You can specify the interval in minutes for # You can specify the interval in minutes for
# rescanning the hosted pages path. By default, # rescanning the hosted pages path. By default,
# this option is disabled, and the pages path # this option is disabled, and the pages path
# will only be scanned on startup. # will only be scanned on startup.
# page_refresh_interval = 0 # page_refresh_interval = 0
# You can specify the interval in minutes for # You can specify the interval in minutes for
# rescanning the hosted files path. By default, # rescanning the hosted files path. By default,
# this option is disabled, and the files path # this option is disabled, and the files path
# will only be scanned on startup. # will only be scanned on startup.
# file_refresh_interval = 0 # file_refresh_interval = 0
[printing] [printing]
@@ -1231,6 +1302,7 @@ max_transfer_size = 256
# various kinds of information and messages. # various kinds of information and messages.
# Printing messages is disabled by default # Printing messages is disabled by default
print_messages = No print_messages = No
# You can configure a custom template for # You can configure a custom template for
@@ -1238,24 +1310,29 @@ print_messages = No
# option, set a path to the template and # option, set a path to the template and
# restart Nomad Network, a default template # restart Nomad Network, a default template
# will be created that you can edit. # will be created that you can edit.
# message_template = ~/.nomadnetwork/print_template_msg.txt # message_template = ~/.nomadnetwork/print_template_msg.txt
# You can configure Nomad Network to only # You can configure Nomad Network to only
# print messages from trusted destinations. # print messages from trusted destinations.
# print_from = trusted # print_from = trusted
# Or specify the source LXMF addresses that # Or specify the source LXMF addresses that
# will automatically have messages printed # will automatically have messages printed
# on arrival. # on arrival.
# print_from = 76fe5751a56067d1e84eef3e88eab85b, 0e70b5848eb57c13154154feaeeb89b7 # print_from = 76fe5751a56067d1e84eef3e88eab85b, 0e70b5848eb57c13154154feaeeb89b7
# Or allow printing from anywhere, if you # Or allow printing from anywhere, if you
# are feeling brave and adventurous. # are feeling brave and adventurous.
# print_from = everywhere # print_from = everywhere
# You can configure the printing command. # You can configure the printing command.
# This will use the default CUPS printer on # This will use the default CUPS printer on
# your system. # your system.
print_command = lp print_command = lp
# You can specify what printer to use # You can specify what printer to use

View File

@@ -1 +1 @@
__version__ = "0.8.0" __version__ = "0.9.1"

View File

@@ -697,7 +697,7 @@ class Browser:
def confirmed(sender): def confirmed(sender):
try: try:
self.retrieve_url(e_url.get_edit_text()) self.retrieve_url(e_url.get_edit_text().strip())
except Exception as e: except Exception as e:
self.browser_footer = urwid.Text("Could not open link: "+str(e)) self.browser_footer = urwid.Text("Could not open link: "+str(e))
self.frame.contents["footer"] = (self.browser_footer, self.frame.options()) self.frame.contents["footer"] = (self.browser_footer, self.frame.options())

View File

@@ -331,7 +331,7 @@ class ConversationsDisplay():
existing_conversations = nomadnet.Conversation.conversation_list(self.app) existing_conversations = nomadnet.Conversation.conversation_list(self.app)
display_name = e_name.get_edit_text() display_name = e_name.get_edit_text()
source_hash_text = e_id.get_edit_text() source_hash_text = e_id.get_edit_text().strip()
source_hash = bytes.fromhex(source_hash_text) source_hash = bytes.fromhex(source_hash_text)
trust_level = DirectoryEntry.UNTRUSTED trust_level = DirectoryEntry.UNTRUSTED
if r_unknown.state == True: if r_unknown.state == True:
@@ -412,7 +412,7 @@ class ConversationsDisplay():
try: try:
local_delivery_signal = "local_delivery_occurred" local_delivery_signal = "local_delivery_occurred"
duplicate_signal = "duplicate_lxm" duplicate_signal = "duplicate_lxm"
lxm_uri = e_uri.get_edit_text() lxm_uri = e_uri.get_edit_text().strip()
ingest_result = self.app.message_router.ingest_lxm_uri( ingest_result = self.app.message_router.ingest_lxm_uri(
lxm_uri, lxm_uri,

View File

@@ -1843,15 +1843,24 @@ class LXMFPeerEntry(urwid.WidgetWrap):
style = "list_unresponsive" style = "list_unresponsive"
focus_style = "list_focus_unresponsive" focus_style = "list_focus_unresponsive"
if peer.propagation_transfer_limit: if peer.propagation_transfer_limit: txfer_limit = RNS.prettysize(peer.propagation_transfer_limit*1000)
txfer_limit = RNS.prettysize(peer.propagation_transfer_limit*1000) else: txfer_limit = "No"
else:
txfer_limit = "No" if peer.propagation_sync_limit: sync_limit = RNS.prettysize(peer.propagation_sync_limit*1000)
else: sync_limit = "Unknown"
if peer.propagation_stamp_cost: sct = peer.propagation_stamp_cost
else: sct = "Unknown"
if peer.propagation_stamp_cost_flexibility: scf = f" (flex {peer.propagation_stamp_cost_flexibility})"
else: scf = ""
ar = round(peer.acceptance_rate*100, 2) ar = round(peer.acceptance_rate*100, 2)
peer_info_str = sym+" "+display_str+"\n "+alive_string+", last heard "+pretty_date(int(peer.last_heard)) peer_info_str = sym+" "+display_str+"\n "+alive_string+", last heard "+pretty_date(int(peer.last_heard))
peer_info_str += "\n "+str(peer.unhandled_message_count)+f" unhandled LXMs, {txfer_limit} sync limit\n" peer_info_str += f"\n {sync_limit} sync limit, {txfer_limit} msg limit"
peer_info_str += f" {RNS.prettyspeed(peer.sync_transfer_rate)} STR, " peer_info_str += f"\n {RNS.prettyspeed(peer.sync_transfer_rate)} STR, {RNS.prettyspeed(peer.link_establishment_rate)} LER"
peer_info_str += f"{RNS.prettyspeed(peer.link_establishment_rate)} LER, {ar}% AR\n" peer_info_str += f"\n Propagation cost {sct}{scf}"
peer_info_str += "\n "+str(peer.unhandled_message_count)+f" unhandled LXMs, {ar}% AR"
widget = ListEntry(peer_info_str) widget = ListEntry(peer_info_str)
self.display_widget = urwid.AttrMap(widget, style, focus_style) self.display_widget = urwid.AttrMap(widget, style, focus_style)
self.display_widget.destination_hash = destination_hash self.display_widget.destination_hash = destination_hash

View File

@@ -30,6 +30,6 @@ setuptools.setup(
entry_points= { entry_points= {
'console_scripts': ['nomadnet=nomadnet.nomadnet:main'] 'console_scripts': ['nomadnet=nomadnet.nomadnet:main']
}, },
install_requires=["rns>=1.0.0", "lxmf>=0.8.0", "urwid>=2.6.16", "qrcode"], install_requires=["rns>=1.0.1", "lxmf>=0.9.1", "urwid>=2.6.16", "qrcode"],
python_requires=">=3.7", python_requires=">=3.7",
) )