aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-14 14:25:44 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-14 14:25:44 +0200
commit1c96aceb54d66cae1b48aa0eb8887f68e53f9e24 (patch)
tree4af4bc59bc231c7cef0237a3fb6dfd7ea38fd823 /packages/meshbay-node
parent86563d5db0ae19fc58336b71a5c29a8712973590 (diff)
downloadmeshbay-1c96aceb54d66cae1b48aa0eb8887f68e53f9e24.tar.gz
perf(client): bundle KDF to 128 MB, and derive it once per sign-in
Argon2id memory 64 → 128 MB. Memory is the lever, not time: it caps how many guesses a card can hold at once, so the ceiling on one high-end GPU moves from roughly 4k to roughly 2k guesses/s and its 24 GB fits ~187 lanes instead of ~375. Measured through the vendored build: 640 ms, against 322 ms at 64 MB. While measuring the real cost of a sign-in, found the SPA deriving the bundle key twice — once for the key pair kept for the session, then again inside decryptBundle() for the local bundle. At these parameters that is 0.6 s of pure waste. Measured now, end to end: auth_key (PBKDF2 600k) 239 ms bundle v1 (PBKDF2 600k) 240 ms legacy, until every bundle is upgraded bundle v2 (Argon2id 128MB) 650 ms ----------------------------------- sign-in 1 129 ms (889 ms once no v1 bundles remain) Once per sign-in, and only then: reopening a group, downloading, streaming and reloading the page all reuse the key, which lives in IndexedDB from login. Also bounds two waits in the node's hub WebSocket, found because the node went silent again mid-deploy. It had reconnected after the hub restart, sent its auth frame, and waited for a reply that never came — `ws.recv()` had no timeout, so a hub that accepts a socket and then says nothing for a few seconds while starting up parks the task forever: node running, logging nothing, invisible to everyone. The auth exchange now times out at 15 s, connect at 15 s, and a refused auth retries with a fresh token instead of ending the task for good. QE harness signs in once per account and reuses the token — several clients there stand for several browsers of one person, and what tells them apart is which keys they hold, not which token, while the hub quite rightly rate-limits repeated logins from one address. Tests: 341, plus the live workflow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node')
-rw-r--r--packages/meshbay-node/src/meshbay_node/hub_client.py17
1 files changed, 14 insertions, 3 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/hub_client.py b/packages/meshbay-node/src/meshbay_node/hub_client.py
index 691ac7c..334107d 100644
--- a/packages/meshbay-node/src/meshbay_node/hub_client.py
+++ b/packages/meshbay-node/src/meshbay_node/hub_client.py
@@ -241,6 +241,7 @@ class HubClient:
# working one until someone notices the node has vanished.
async with websockets.connect(
ws_url, ping_interval=20, ping_timeout=20, close_timeout=5,
+ open_timeout=15,
) as ws:
auth_msg = {
"type": "auth",
@@ -250,10 +251,20 @@ class HubClient:
if group_ids:
auth_msg["group_ids"] = group_ids
await ws.send(json.dumps(auth_msg))
- auth_resp = json.loads(await ws.recv())
+ # Bounded: a hub that accepts the socket and then says nothing
+ # — which is what it does for a few seconds while restarting —
+ # would otherwise park this task here forever, with the node
+ # running, silent, and invisible to everyone.
+ auth_resp = json.loads(
+ await asyncio.wait_for(ws.recv(), timeout=15))
if auth_resp.get("type") != "auth_ok":
- log.error("WS auth failed: %s", auth_resp)
- return
+ # Not fatal: the token may simply have expired while we
+ # were disconnected. Refresh on the next pass rather than
+ # ending the task, which used to strand the node for good.
+ log.warning("WS auth refused: %s — retrying in 5s", auth_resp)
+ await asyncio.sleep(5)
+ await self.ensure_fresh_token()
+ continue
self._ws = ws
log.info("Hub WS connected")