Repository navigation
Description
Affected layers
- Backend (
server/): affected.TelegramServiceinserver/src/telegram.tsbumps a connection generation on every connect, pair and disconnect, and work that awaited the network checks it before acting, so that a disconnect cannot be undone by a slow request. Two paths skip that check:initializemarks the saved settings as loaded only after readingtelegram.json, so two first calls both read the file, and the slower one installs the saved pairing over whatever happened in between, including a disconnect, a new bot or a new pairing.- The
/statuscommand handler captures the paired chat, awaits the activity snapshot, which reads Docker and the monitoring APIs, and then sends the summary to that chat without checking whether it was disconnected in the meantime.
- Frontend (
src/): not affected. The Telegram card insrc/components/settings/ExperimentalTab.tsxcalls the disconnect route and shows what the backend reports; it keeps no pairing state of its own. shared/: not affected.
Problem
After a slower first load restores a pairing that a disconnect removed, the service polls the old bot again, answers the old chat's /status and /settings commands, and writes the pairing back to telegram.json the next time that chat sends a command. The settings page shows the old chat as paired again. In the current wiring this cannot happen: the backend starts the first load the moment it listens, and every other caller needs a session, which a restart clears, so the first load has finished long before anyone can log in and disconnect. It becomes reachable if the first load starts later, or if another caller is added.
Separately, a /status that arrived just before a disconnect is answered after it, with a snapshot collected at that moment.
Both reproduce on main at 828dad4 with the Loupe tests under Node 24: after the delayed load, the settings report the disconnected chat as paired, and a /status held across a disconnect is still sent.
Impact
An operator disconnects a chat to stop it receiving mining data: the bot was handed to someone else, the chat is no longer trusted, or the instance is being reset, which disconnects as well. Through the /status path that chat receives at most one more summary, with the pool name, hashrate, workers, shares and best difficulty it received seconds earlier; sending more commands does not widen it, because each check takes one snapshot and stops at the first disconnect. Through the initialization path the old chat would keep receiving alerts and summaries and could change alert settings indefinitely, but that path is not reachable today. Telegram is opt-in, so only instances with a paired bot are affected. Loupe rates #110 medium and #112 low.
Expected outcome
- Once a disconnect has returned, the former chat receives no further message from the backend, including replies to commands it sent before the disconnect.
- A removed pairing never comes back, in the running service or in
telegram.json, unless a new pairing link is used. - A connect, pair or disconnect made while the backend is still loading its saved Telegram settings is never undone when that load finishes.
Loupe findings
- project-loupe/audit-sv2-ui#110: a slower first load of the Telegram settings restores a pairing that a disconnect removed
- project-loupe/audit-sv2-ui#112: a
/statusreply awaiting i 74EB ts snapshot is sent to a chat disconnected in the meantime
Related issues and PRs
- Add Experimental Telegram integration to get notifications (block found, best diff, workers connections) #228 added the Telegram service and its connection generation, which the other network-awaiting paths already check.
- The initialization race was raised during the Add Experimental Telegram integration to get notifications (block found, best diff, workers connections) #228 review, in an inline comment on
initialize; that thread is still open. - Tracker: [Tracker] Telegram notifications #402.
Activity