diff options
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/config.py')
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/config.py | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/config.py b/packages/meshbay-node/src/meshbay_node/config.py index 086d800..2ae3c7c 100644 --- a/packages/meshbay-node/src/meshbay_node/config.py +++ b/packages/meshbay-node/src/meshbay_node/config.py @@ -67,6 +67,13 @@ max_concurrent_streams = 8 max_concurrent_downloads = 8 max_concurrent_uploads = 8 +# The largest single file a member may upload to this node, in GB. It is this +# machine's disk that fills, so the ceiling is the operator's to set: lower it +# on a small disk, raise it for a library of films. Fractions are allowed +# (0.5 = 512 MB). A file past it is refused at the chunk that crosses it and +# the partial file is deleted. +max_upload_gb = 8 + # HEVC sources have no browser decoder on most platforms, so streaming one is # transcoded to H264 rather than the usual free copy — real CPU per viewer. # Set to false only if every viewer's client is known to decode HEVC itself. @@ -181,6 +188,11 @@ class NodeConfig: # answer "server busy". See meshbay_node/transfers.py. max_concurrent_downloads: int = 8 max_concurrent_uploads: int = 8 + # The per-file upload ceiling, in GB — the one limit here that bounds a + # member's writes to the operator's disk rather than this node's own + # concurrency. There is still no aggregate quota (MESHBAY_DESIGN.md + # §15.3), so this is what stands between a writable root and a full disk. + max_upload_gb: float = 8.0 # HEVC (and any future codec in media_probe.py's # BROWSER_INCOMPATIBLE_VIDEO_CODECS) has no decoder in most browsers, so # streaming it needs a real re-encode to H264 rather than the usual free @@ -301,6 +313,28 @@ def _positive(value: object, default: int, name: str) -> int: return n +def _positive_float(value: object, default: float, name: str) -> float: + """A size that must be greater than zero, or the default with a word about it. + + Separate from `_positive` because this one is a quantity, not a count: half + a gigabyte is a legitimate ceiling on a small disk, and rounding it to zero + would refuse every upload with nothing in the log to say why. + """ + if isinstance(value, bool) or not isinstance(value, (int, float, str)): + log.warning("%s = %r is not a size — using %g", name, value, default) + return default + try: + n = float(value) + except (TypeError, ValueError): + log.warning("%s = %r is not a number — using %g", name, value, default) + return default + if n <= 0: + log.warning("%s = %g would refuse every upload — using %g", + name, n, default) + return default + return n + + def _read_roots(group: dict) -> list[RootSpec]: """ A group's roots, from `[[groups.roots]]` or from the legacy `shared_dir`. @@ -332,6 +366,35 @@ def _read_roots(group: dict) -> list[RootSpec]: return specs +def node_settings_defaults(nd: NodeConfig | None = None) -> dict: + """ + The `node.toml` side of every setting the roster resolves. + + One function because there were three copies of this dict written by hand + and they disagreed. The daemon's left out both transfer pools, so on a node + whose operator had never touched the panel they resolved to None, were + written back onto the config, and the transport fell through to its own + defaults — `node.toml` parsed, validated, and then ignored. + + `test_node_settings_defaults.py` holds this against + `Roster.node_setting_keys()`, because a key missing here raises nothing + anywhere: it is a setting that stops working quietly. + """ + nd = nd or NodeConfig() + return { + "invite_ttl_hours": nd.invite_ttl_hours, + "pair_ttl_hours": nd.pair_ttl_hours, + "device_request_ttl_minutes": nd.device_request_ttl_minutes, + "max_concurrent_streams": nd.max_concurrent_streams, + "max_concurrent_downloads": nd.max_concurrent_downloads, + "max_concurrent_uploads": nd.max_concurrent_uploads, + "max_upload_gb": nd.max_upload_gb, + "transcode_incompatible_video": nd.transcode_incompatible_video, + "stun_servers": nd.stun_servers or list(DEFAULT_STUN_SERVERS), + "ice_interfaces": nd.ice_interfaces, + } + + def load_config(path: Path = DEFAULT_CONFIG_PATH) -> Config: """ Load config from TOML file. Supports both single [group] and @@ -368,6 +431,9 @@ def load_config(path: Path = DEFAULT_CONFIG_PATH) -> Config: cfg.node.max_concurrent_uploads = _positive( nd.get("max_concurrent_uploads", cfg.node.max_concurrent_uploads), cfg.node.max_concurrent_uploads, "max_concurrent_uploads") + cfg.node.max_upload_gb = _positive_float( + nd.get("max_upload_gb", cfg.node.max_upload_gb), + cfg.node.max_upload_gb, "max_upload_gb") cfg.node.transcode_incompatible_video = bool( nd.get("transcode_incompatible_video", cfg.node.transcode_incompatible_video)) cfg.node.hardware_video_encode = bool( |