aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/roster.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-19 14:01:38 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-19 14:01:38 +0200
commitd2495a2c4b89fbbfc18cefec83ae96cabdd745e2 (patch)
tree463e4d11b1c742475aac07155818c9c64a1e43e5 /packages/meshbay-node/src/meshbay_node/roster.py
parentf8223293a211a87c92b1fed80f5ca53660f6b26c (diff)
parent933daccbcfde7705413d3a10db87d910c650ed42 (diff)
downloadmeshbay-d2495a2c4b89fbbfc18cefec83ae96cabdd745e2.tar.gz
Merge origin/main: the operator's upload ceiling beside the disk-thread work
One conflict, in §15.3's open list, and it was two changes agreeing rather than disagreeing: this side removed the rows for the third-party search bound and the node-announcement bound because both are now built (AV27, AV28), while the other side kept them and added a new one. Resolved by keeping what is genuinely still open — per-device revocation having no CLI — and leaving the two closed. `webrtc_server.py` merged without conflict but the two sides met inside one function: `_upload_chunk` gained the operator's `max_upload_gb` ceiling from there and the per-group lock and `off_disk` calls from here. Read back rather than trusted: the operator's ceiling now sits inside the critical section that keeps chunk ordering, and the unlink beside it goes to the disk thread with everything else. 2893 passed. The twelve `test_sticky_header.py[firefox]` setup errors are the open Firefox on this machine, as before.
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/roster.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/roster.py53
1 files changed, 39 insertions, 14 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/roster.py b/packages/meshbay-node/src/meshbay_node/roster.py
index 064ab93..8c9b3ef 100644
--- a/packages/meshbay-node/src/meshbay_node/roster.py
+++ b/packages/meshbay-node/src/meshbay_node/roster.py
@@ -983,35 +983,60 @@ class Roster:
SETTING_MAX_STREAMS = "max_concurrent_streams"
SETTING_MAX_DOWNLOADS = "max_concurrent_downloads"
SETTING_MAX_UPLOADS = "max_concurrent_uploads"
+ SETTING_MAX_UPLOAD_GB = "max_upload_gb"
SETTING_TRANSCODE = "transcode_incompatible_video"
SETTING_STUN_SERVERS = "stun_servers"
SETTING_ICE_INTERFACES = "ice_interfaces"
+ # Every setting this resolver answers for, and how a stored string becomes
+ # a value. `node_settings` iterates these two and nothing else, and
+ # `config.node_settings_defaults` is built from the same names — so the
+ # defaults dict cannot quietly cover fewer settings than are resolved.
+ #
+ # It did. The daemon's dict was written by hand and omitted the two
+ # transfer pools, so on a node with no panel override they resolved to
+ # `defaults.get(key)` → None, were assigned back onto the config, and the
+ # transport skipped them: `node.toml` was parsed, validated, and then
+ # replaced by the transport's own defaults. A missing key is not an error
+ # anywhere along that path — it is a setting that stops working in silence,
+ # and invisible unless the operator picked a value other than the default.
+ NODE_SETTING_SCALARS: tuple[tuple[str, str, str], ...] = (
+ ("invite_ttl_hours", SETTING_INVITE_TTL, "int"),
+ ("pair_ttl_hours", SETTING_PAIR_TTL, "int"),
+ ("device_request_ttl_minutes", SETTING_DEVICE_TTL, "int"),
+ ("max_concurrent_streams", SETTING_MAX_STREAMS, "int"),
+ ("max_concurrent_downloads", SETTING_MAX_DOWNLOADS, "int"),
+ ("max_concurrent_uploads", SETTING_MAX_UPLOADS, "int"),
+ ("transcode_incompatible_video", SETTING_TRANSCODE, "bool"),
+ ("max_upload_gb", SETTING_MAX_UPLOAD_GB, "float"),
+ )
+ NODE_SETTING_LISTS: tuple[tuple[str, str], ...] = (
+ ("stun_servers", SETTING_STUN_SERVERS),
+ ("ice_interfaces", SETTING_ICE_INTERFACES),
+ )
+
+ @classmethod
+ def node_setting_keys(cls) -> frozenset[str]:
+ """Every key `node_settings` returns — what a defaults dict must cover."""
+ return frozenset([k for k, _, _ in cls.NODE_SETTING_SCALARS]
+ + [k for k, _ in cls.NODE_SETTING_LISTS])
+
async def node_settings(self, defaults: dict) -> dict:
"""Current effective settings: roster override if present, else config default."""
import json as _json
result = {}
- for key, setting in [
- ("invite_ttl_hours", self.SETTING_INVITE_TTL),
- ("pair_ttl_hours", self.SETTING_PAIR_TTL),
- ("device_request_ttl_minutes", self.SETTING_DEVICE_TTL),
- ("max_concurrent_streams", self.SETTING_MAX_STREAMS),
- ("max_concurrent_downloads", self.SETTING_MAX_DOWNLOADS),
- ("max_concurrent_uploads", self.SETTING_MAX_UPLOADS),
- ("transcode_incompatible_video", self.SETTING_TRANSCODE),
- ]:
+ for key, setting, kind in self.NODE_SETTING_SCALARS:
stored = await self.get_setting(self.NODE_WIDE_GROUP_ID, setting)
if stored is not None:
- if key == "transcode_incompatible_video":
+ if kind == "bool":
result[key] = stored != "0"
+ elif kind == "float":
+ result[key] = float(stored)
else:
result[key] = int(stored)
else:
result[key] = defaults.get(key)
- for list_key, setting in [
- ("stun_servers", self.SETTING_STUN_SERVERS),
- ("ice_interfaces", self.SETTING_ICE_INTERFACES),
- ]:
+ for list_key, setting in self.NODE_SETTING_LISTS:
stored = await self.get_setting(self.NODE_WIDE_GROUP_ID, setting)
if stored is not None:
try: