aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/config.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-09 04:12:34 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-09 04:12:34 +0200
commitece4b01405b8edcf5cbe6367a2882433db1491b5 (patch)
tree339844449dc68a9675da47195665ad7d3d6fced7 /packages/meshbay-node/src/meshbay_node/config.py
parent6abb68ae95f6c4da4a66453398006183a73db9d9 (diff)
downloadmeshbay-ece4b01405b8edcf5cbe6367a2882433db1491b5.tar.gz
feat(node): add config, daemon, and local web UI
config.py: TOML + env var overrides, sane defaults. daemon.py: full startup sequence (keystore→hub→GEK→indexer→ server→UI), SIGINT/SIGTERM shutdown, calibrate-argon2 command. ui/app.py: FastAPI on localhost:18000, status+files JSON API, HTML status page (auto-refresh 10s). All bound to 127.0.0.1. Full suite: 29/29 tests. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/config.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/config.py123
1 files changed, 123 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/config.py b/packages/meshbay-node/src/meshbay_node/config.py
new file mode 100644
index 0000000..27301af
--- /dev/null
+++ b/packages/meshbay-node/src/meshbay_node/config.py
@@ -0,0 +1,123 @@
+"""
+MeshBay Node configuration.
+
+Config file: ~/.config/meshbay/node.toml
+All values have sensible defaults and can be overridden by env vars
+prefixed with MESHBAY_ (e.g. MESHBAY_HUB_URL).
+"""
+
+import os
+from dataclasses import dataclass, field
+from pathlib import Path
+
+try:
+ import tomllib # Python 3.11+
+except ImportError:
+ import tomli as tomllib # type: ignore[no-redef]
+
+DEFAULT_CONFIG_PATH = Path.home() / ".config" / "meshbay" / "node.toml"
+
+EXAMPLE_CONFIG = """\
+# MeshBay Node configuration
+# See: https://meshbay.org/docs/node-config
+
+[hub]
+url = "http://meshbay.org"
+username = "myusername"
+# password stored separately via keystore
+
+[node]
+port = 19000
+ui_port = 18000
+shared_dirs = ["/home/user/MeshBay"]
+
+[group]
+id = "" # set after joining a group
+name = ""
+
+[keystore]
+# unlock_file = "~/.config/meshbay/unlock.key"
+# or set MESHBAY_UNLOCK_KEY env var
+"""
+
+
+@dataclass
+class HubConfig:
+ url: str = "http://meshbay.org"
+ username: str = ""
+ password: str = "" # loaded from keystore or env; never written to TOML
+
+
+@dataclass
+class NodeConfig:
+ port: int = 19000
+ ui_port: int = 18000
+ shared_dirs: list[str] = field(default_factory=list)
+
+
+@dataclass
+class GroupConfig:
+ id: str = ""
+ name: str = ""
+
+
+@dataclass
+class KeystoreConfig:
+ path: Path = field(default_factory=lambda: DEFAULT_CONFIG_PATH.parent / "keystore.enc")
+ unlock_file: Path | None = None
+
+
+@dataclass
+class Config:
+ hub: HubConfig = field(default_factory=HubConfig)
+ node: NodeConfig = field(default_factory=NodeConfig)
+ group: GroupConfig = field(default_factory=GroupConfig)
+ keystore: KeystoreConfig = field(default_factory=KeystoreConfig)
+
+
+def load_config(path: Path = DEFAULT_CONFIG_PATH) -> Config:
+ """
+ Load config from TOML file. Missing file returns defaults.
+ Env vars override file values.
+ """
+ cfg = Config()
+
+ if path.exists():
+ raw = tomllib.loads(path.read_text())
+ hub = raw.get("hub", {})
+ cfg.hub.url = hub.get("url", cfg.hub.url)
+ cfg.hub.username = hub.get("username", cfg.hub.username)
+
+ nd = raw.get("node", {})
+ cfg.node.port = nd.get("port", cfg.node.port)
+ cfg.node.ui_port = nd.get("ui_port", cfg.node.ui_port)
+ cfg.node.shared_dirs = nd.get("shared_dirs", cfg.node.shared_dirs)
+
+ grp = raw.get("group", {})
+ cfg.group.id = grp.get("id", cfg.group.id)
+ cfg.group.name = grp.get("name", cfg.group.name)
+
+ ks = raw.get("keystore", {})
+ if "path" in ks:
+ cfg.keystore.path = Path(ks["path"]).expanduser()
+ if "unlock_file" in ks:
+ cfg.keystore.unlock_file = Path(ks["unlock_file"]).expanduser()
+
+ # Env var overrides
+ if url := os.environ.get("MESHBAY_HUB_URL"):
+ cfg.hub.url = url
+ if user := os.environ.get("MESHBAY_USERNAME"):
+ cfg.hub.username = user
+ if pwd := os.environ.get("MESHBAY_PASSWORD"):
+ cfg.hub.password = pwd
+ if port := os.environ.get("MESHBAY_PORT"):
+ cfg.node.port = int(port)
+
+ return cfg
+
+
+def write_example_config(path: Path = DEFAULT_CONFIG_PATH) -> None:
+ """Write an example config file if none exists."""
+ if not path.exists():
+ path.parent.mkdir(parents=True, exist_ok=True)
+ path.write_text(EXAMPLE_CONFIG)