From 78639260932dbd679d0de1f3132c645440962f46 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 13 Aug 2026 15:40:06 +0200 Subject: feat(node): keep the daemon alive when the hub rejects credentials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A node whose owner has not registered yet got a plain 401 from /v1/nodes/auth, which _login_with_retry re-raised — so the daemon exited and took its local admin UI down with it. That UI is where the operator reads the node's public key in order to link it, so exiting strands them: no daemon, no key, no way forward without digging the keystore open by hand. The daemon already parks on "No node key" for exactly this reason; it now parks on any 401, reporting waiting_for_account with a message naming the account and hub, and keeps retrying every 30s. The intended order remains: register on the hub, install the node, copy its key from the local UI, paste it into Settings > Link Node. The daemon now survives being started out of order instead of failing with a traceback. Adds QE/deploy/ — generic deployment (deploy-hub.sh, deploy-node.sh) kept separate from the demo scenario (demo.py, demo.env, README.md). Credentials live in QE/, which is gitignored; verified with git check-ignore. Co-Authored-By: Claude Opus 5 --- packages/meshbay-node/src/meshbay_node/daemon.py | 30 +++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'packages/meshbay-node/src') diff --git a/packages/meshbay-node/src/meshbay_node/daemon.py b/packages/meshbay-node/src/meshbay_node/daemon.py index fa8c0ee..bedd2e9 100644 --- a/packages/meshbay-node/src/meshbay_node/daemon.py +++ b/packages/meshbay-node/src/meshbay_node/daemon.py @@ -390,14 +390,28 @@ class NodeDaemon: return await hub.startup(endpoint_hint=None) except _httpx.HTTPStatusError as e: body = e.response.text if hasattr(e.response, 'text') else '' - if e.response.status_code == 401 and "No node key" in body: - self._state["status"] = "waiting_for_node_key" - log.warning( - "Node key not linked — open admin UI at " - "http://localhost:%d, copy the key, and paste it in " - "Settings > Link Node on the hub. Retrying in 30s...", - self._config.node.ui_port, - ) + # Any 401 here needs a human at a browser, and the operator needs + # this daemon alive to read its public key out of the local admin + # UI. Exiting would take that UI down and strand them — which is + # exactly what happened when a node was started before its owner + # had registered. + if e.response.status_code == 401: + if "No node key" in body: + self._state["status"] = "waiting_for_node_key" + log.warning( + "Node key not linked. Open the admin UI, copy this " + "node's key, and paste it in Settings > Link Node on " + "%s. Retrying in 30s...", + self._config.hub.url, + ) + else: + self._state["status"] = "waiting_for_account" + log.warning( + "Hub rejected the node credentials for user %r. " + "Register that account on %s first, then link this " + "node's key. Retrying in 30s...", + self._config.hub.username, self._config.hub.url, + ) await asyncio.sleep(30) else: raise -- cgit v1.2.3