aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/hub_client.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-11 22:11:18 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-11 22:11:18 +0200
commitbbfc45925e82c364519b9d796758003365bc9005 (patch)
treed635473b6eea3d342b013c67552c52187e954e6c /packages/meshbay-node/src/meshbay_node/hub_client.py
parent3015c631883b8289369bb29a3842a3971e608170 (diff)
downloadmeshbay-bbfc45925e82c364519b9d796758003365bc9005.tar.gz
feat(node): Phase 11 — production-ready daemon with WebRTC, WS, chat, HTTP
The node daemon was previously a skeleton that only started QUIC/TCP servers and the local web UI. All browser-facing functionality (WebRTC, hub WebSocket, chat store, HTTP file API) lived in QE demo scripts. This rewrites daemon.py to be fully self-contained: - WebRTC transport for browser clients (aiortc DataChannel) - Hub WebSocket task (signaling, revocations, WebRTC offers) - ChatStore per group (SQLite in ~/.local/share/meshbay/) - HTTP file API per group (create_http_app on configured port) - Graceful shutdown (all transports, stores, tasks) - hub_client: _ws tracking + send_ws() for chat notifications - config: data_dir field for persistent state - systemd: security hardening (ProtectSystem, StateDirectory) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/hub_client.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/hub_client.py16
1 files changed, 15 insertions, 1 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/hub_client.py b/packages/meshbay-node/src/meshbay_node/hub_client.py
index a9a1d6c..ca0e776 100644
--- a/packages/meshbay-node/src/meshbay_node/hub_client.py
+++ b/packages/meshbay-node/src/meshbay_node/hub_client.py
@@ -81,6 +81,7 @@ class HubClient:
self._keys = keys
self._http = httpx.AsyncClient(timeout=15, base_url=config.hub_url)
self._session: HubSession | None = None
+ self._ws: Any = None
async def __aenter__(self):
return self
@@ -246,6 +247,15 @@ class HubClient:
# ── Persistent WebSocket (signaling + revocations) ──────────────────────
+ async def send_ws(self, data: str) -> None:
+ """Send a message on the hub WebSocket (if connected). Best-effort."""
+ ws = self._ws
+ if ws:
+ try:
+ await ws.send(data)
+ except Exception:
+ pass
+
async def maintain_ws(
self,
on_incoming: Any = None,
@@ -282,6 +292,7 @@ class HubClient:
log.error("WS auth failed: %s", auth_resp)
return
+ self._ws = ws
log.info("Hub WS connected")
async for raw in ws:
@@ -310,10 +321,13 @@ class HubClient:
elif mtype == "pong":
pass
+ except asyncio.CancelledError:
+ raise
except Exception as e:
log.warning("Hub WS disconnected: %s — reconnecting in 5s", e)
- import asyncio
await asyncio.sleep(5)
+ finally:
+ self._ws = None
# ── Convenience: full startup sequence ───────────────────────────────────