From 3e36c6f50d5f5d182ae139431270841723c1fc33 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 4 Sep 2026 19:38:27 -0700 Subject: sockets: accept read-only tokens on login, deliver payment events only POS terminals log into the websocket with the "-ro" token that the web flasher pre-fills. track() looked that id up as a user, failed, and closed the socket a second later, dropping the invoice subscription, so devices were listening about one second in six. Strip the suffix the way lib/auth.ts does for HTTP, flag the socket read-only, and forward only "payment" events to it. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_014gVYGi29FjTeLdNTiutkZx --- lib/sockets.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/sockets.ts b/lib/sockets.ts index 58673986..610d38fd 100644 --- a/lib/sockets.ts +++ b/lib/sockets.ts @@ -35,6 +35,9 @@ export const emit = (uid, type, data) => { if (!store.sockets[uid]) return; for (const id in store.sockets[uid]) { const ws = store.sockets[uid][id]; + // Read-only sockets (POS terminals logged in with a "-ro" token) only + // get payment notifications, mirroring the HTTP read-only whitelist. + if (ws.readonly && type !== "payment") continue; ws.send(JSON.stringify({ type, data })); } }; @@ -56,9 +59,16 @@ export const broadcast = (type, data) => { const track = async (ws, token) => { const { id } = ws; - const { id: uid } = verifyToken(token) || {}; + let { id: uid } = verifyToken(token) || {}; if (!uid) fail("Invalid JWT token"); + + // Read-only tokens (see users.ro) carry "-ro". The HTTP auth layer + // strips the suffix for whitelisted routes; do the same here so a POS + // device can log in and receive payment events, but nothing else. + const readonly = uid.endsWith("-ro"); + if (readonly) uid = uid.slice(0, -3); + const user = await getUser(uid); if (!user) fail(`User not found ${uid}`); @@ -73,6 +83,7 @@ const track = async (ws, token) => { store.sockets[uid][id] = ws; users[id] = uid; ws.user = user; + ws.readonly = readonly; }; Bun.serve({ -- cgit v1.2.3