13 KiB
Messenger Connectors - Uncertainty Pass 29
Date: 2026-04-29 Scope: official shared Telegram bot ingress, webhook ACK semantics, offline desktop behavior, and no durable backend plaintext queue
Executive Delta
The next weakest boundary is official bot ingress:
Telegram webhook update
official backend
desktop live connection
durable local turn
Telegram webhook ACK
The product decision was:
Default official bot, no durable backend plaintext queue.
If desktop is offline, be honest and answer offline.
Encrypted queue can be added later as advanced reliability mode.
This creates a precise reliability contract:
Backend must ACK Telegram only after either:
1. desktop durably accepted the plaintext turn locally, or
2. backend recorded a redaction-safe offline/blocked decision and attempted or skipped the offline notice by policy, or
3. this is a duplicate already completed update.
Do not use Telegram webhook retry as the queue. It is operationally noisy, finite, and hard to reason about.
Source Facts Rechecked
Telegram official facts checked on 2026-04-29:
setWebhooksends HTTPS POST updates to our URL.- Telegram repeats webhook delivery after a non-2xx response and eventually gives up after a reasonable number of attempts.
secret_tokencan be configured so Telegram includesX-Telegram-Bot-Api-Secret-Token.max_connectionscan be 1-100 and defaults to 40.drop_pending_updatescan drop pending updates.getWebhookInfoexposespending_update_countand last error fields.getUpdatesconfirms updates by calling with offset greater than the previousupdate_id.- Telegram stores incoming updates until received, but not longer than 24 hours.
getUpdatescannot be used while webhook is set.- Bot API calls made directly in the webhook response do not return a result to us.
Sources:
- https://core.telegram.org/bots/api#setwebhook
- https://core.telegram.org/bots/api#getwebhookinfo
- https://core.telegram.org/bots/api#getting-updates
- https://core.telegram.org/bots/api#making-requests-when-getting-updates
- https://core.telegram.org/bots/faq
Local code facts:
- Existing
HttpServerbinds to localhost by default and serves local app HTTP routes through Fastify. - Existing browser mode uses SSE from local server to renderer.
- There is no current cloud/backend persistent relay layer for messenger traffic.
- The app already has a usable local event idea, but messenger official mode needs a new outbound desktop-to-cloud control connection, not the existing local HTTP server.
1. The ACK Problem
If the backend returns non-2xx to Telegram because desktop is offline, Telegram retries. That sounds like a queue, but it is a bad queue:
- plaintext stays in Telegram's pending delivery mechanism, not under our product semantics;
- retries can repeat the same update while the user keeps typing;
pending_update_countcan grow and hide real bugs;- retries are finite;
- update retention has an upper bound;
- backend may send duplicate offline notices unless it has its own idempotency state.
If the backend returns 2xx before desktop has durably accepted the update, the lead message can be lost forever in official mode because we intentionally do not keep plaintext.
Therefore ACK timing is the core ingress invariant.
2. Recommended Backend Ingress State Machine
Backend should persist only redaction-safe metadata before side effects:
type OfficialIngressReceipt = {
receiptId: string;
provider: 'telegram';
botScope: 'official';
updateId: number;
providerMessageKey: string;
routeId: string | null;
routeGeneration: number | null;
textHash: string | null;
fromUserHash: string | null;
chatIdHash: string;
messageThreadId: number | null;
status:
| 'received'
| 'route_missing'
| 'desktop_claim_started'
| 'desktop_accepted'
| 'desktop_acceptance_unknown'
| 'offline_notice_started'
| 'offline_notice_sent'
| 'offline_notice_ambiguous'
| 'acknowledged'
| 'failed_terminal';
createdAt: string;
updatedAt: string;
};
State rules:
received -> desktop_claim_started -> desktop_accepted -> acknowledged
received -> offline_notice_started -> offline_notice_sent -> acknowledged
received -> offline_notice_started -> offline_notice_ambiguous -> acknowledged
received -> route_missing -> acknowledged
duplicate acknowledged -> acknowledged
Important: offline_notice_ambiguous still ACKs the webhook. It is better to possibly miss the offline notice than to auto-send duplicate notices or keep Telegram retrying.
3. Desktop Claim Protocol
Official mode needs a desktop-initiated persistent connection:
desktop -> backend: connect(route subscriptions, install id, session key, capabilities)
backend -> desktop: inbound plaintext turn
desktop -> backend: accepted_local(internalTurnId, providerMessageKey, localMessageId)
backend -> Telegram: 2xx webhook ACK
Rules:
- Backend forwards plaintext only to an already-authenticated active desktop connection.
- Desktop must persist the turn locally before returning
accepted_local. - Desktop dedupes by
providerMessageKeyand returns the existing local acceptance if the backend retries delivery. - Backend does not store plaintext after the request handler scope.
- Backend stores only hashes and receipt state.
- If no desktop session can accept within a short timeout, backend goes to offline policy.
Suggested timeout:
2-4 seconds for desktop accepted_local
then offline response or offline status
This keeps webhook handlers bounded and avoids Telegram retry storms.
4. Crash Matrix
Critical cases:
- Backend crashes before persisting receipt.
- Telegram retries; safe, because no side effect happened.
- Backend persists receipt, crashes before desktop forward.
- Telegram retries; backend can process again from
received.
- Telegram retries; backend can process again from
- Backend forwards plaintext to desktop, crashes before desktop ACK.
- Telegram retries; desktop dedupe by
providerMessageKeyprevents duplicate local turn.
- Telegram retries; desktop dedupe by
- Desktop persists local turn, ACK to backend is lost.
- Telegram retries; backend redelivers, desktop returns existing
accepted_local.
- Telegram retries; backend redelivers, desktop returns existing
- Backend records
desktop_accepted, crashes before HTTP 2xx.- Telegram retries; backend sees completed receipt and returns 2xx without redelivering.
- Desktop offline.
- Backend records offline decision and ACKs Telegram after offline policy.
- Offline notice
sendMessagesucceeds but backend crashes before marking success.- On retry, backend must not blindly resend. Mark
offline_notice_ambiguous, ACK, show support diagnostics.
- On retry, backend must not blindly resend. Mark
5. Offline Policy
For default official MVP:
No desktop live acceptance = no local delivery.
Then choose one of two offline UX policies:
- Send a short Telegram offline notice.
- ACK silently and rely on topic status / setup UI.
I recommend a short offline notice, but only through the same provider outbox ambiguity policy from pass 42.
Example behavior:
Agent Teams desktop is offline. Open the app on the connected computer and send the message again.
Do not store the lead's plaintext for later replay.
6. Why Not Use Telegram As The Queue
Top risks:
- Telegram will retry on non-2xx, but the retry schedule and final give-up behavior are provider-controlled.
pending_update_countbecomes an operational failure queue with plaintext updates we cannot inspect safely.- Once we finally ACK, Telegram considers the update handled, even if desktop state is not coherent.
- If webhook is reconfigured with
drop_pending_updates, lead messages can be intentionally discarded. - If the app is offline for more than Telegram retention, messages are lost anyway.
This conflicts with the product's "honest offline" behavior.
7. Official Mode Privacy Story
The honest statement:
Official shared bot backend sees message plaintext transiently while handling Telegram delivery.
It does not durably store plaintext in MVP.
It stores redaction-safe delivery metadata and hashes for dedupe, abuse prevention, and diagnostics.
Not honest:
Our backend never sees messages.
That statement is only true for private own-bot local polling mode, not official shared bot mode.
8. Own Bot Contrast
Own bot mode is much simpler for ingress:
desktop getUpdates
desktop durable local turn
desktop confirms offset
Because Telegram getUpdates confirms by offset, desktop can persist locally before advancing offset. That is a better privacy and reliability story for users who want it.
But own bot mode is less convenient because the user must create/configure a bot.
9. Desktop To Backend Transport Options
-
Persistent WebSocket from desktop to backend - 🎯 8 🛡️ 8 🧠 7 - approx
1600-3600changed LOC. Best default for official mode. Full duplex, explicit ACK messages, connection leases, heartbeats, route subscriptions. -
Server-Sent Events from backend to desktop plus HTTPS POST ACKs - 🎯 7 🛡️ 7 🧠 6 - approx
1300-3000changed LOC. Simpler in some networks, but ACK correlation and reconnect handling are more awkward. -
Desktop polling backend every N seconds - 🎯 5 🛡️ 5 🧠 4 - approx
700-1800changed LOC. Poor fit for no plaintext queue because backend would need to hold plaintext or lead messages would be missed between polls.
Recommendation:
Use WebSocket-like persistent desktop claim channel for official mode.
Do not add a package decision until package versions can be verified in an unrestricted network environment.
10. Multi-Desktop And Lease Policy
If the same user connects the same official route from multiple desktops:
Only one active receiver lease may own inbound plaintext delivery.
Options:
-
Single primary device per route - 🎯 8 🛡️ 8 🧠 5 - approx
600-1400changed LOC. Recommended for MVP. Simple and prevents split-brain delivery. -
Fan out to all active desktops and accept first durable ACK - 🎯 6 🛡️ 6 🧠 7 - approx
1200-2600changed LOC. Can duplicate local inboxes and confuse reply ownership. -
Per-team device assignment - 🎯 7 🛡️ 8 🧠 7 - approx
1400-3200changed LOC. Useful later for power users, too much for MVP.
11. Security Requirements
Minimum official ingress controls:
- Verify
X-Telegram-Bot-Api-Secret-Token. - Use a secret webhook path as defense in depth.
- Reject updates that do not match expected bot id/account binding.
- Use
allowed_updatesto narrow update surface. - Persist update id/provider message key dedupe.
- Rate-limit offline notices by chat/topic.
- HMAC/hash user ids and chat ids in backend logs.
- Do not log plaintext update payloads.
- Encrypt desktop-backend transport.
- Rotate desktop session tokens.
12. Test Matrix
Tests should simulate:
- valid webhook with active desktop accepted;
- duplicate webhook update after accepted;
- backend crash before receipt write;
- backend crash after receipt write;
- backend crash after desktop forward;
- desktop accepted locally but ACK lost;
- backend accepted desktop ACK but HTTP 2xx lost;
- desktop offline;
- offline notice success;
- offline notice timeout after request start;
- webhook secret mismatch;
- route missing;
- route disabled;
- topic deleted;
- bot permission lost;
- two desktop sessions racing;
- webhook max_connections concurrent deliveries out of order;
drop_pending_updatesduring reconnect;- old update after allowed_updates change;
- backend store unavailable;
- desktop reconnect while webhook is in-flight.
Pass criterion:
No plaintext is durably stored by official backend.
No Telegram update is ACKed as handled before either desktop durable acceptance or an explicit offline/blocked decision.
No duplicate local turns for the same providerMessageKey.
No duplicate offline notice unless user/support explicitly chooses duplicate send.
13. Top 3 Overall Options
-
Synchronous desktop claim + redaction-safe ingress receipt + offline notice outbox - 🎯 8 🛡️ 8 🧠 8 - approx
2500-6000changed LOC. Recommended official MVP. It matches "no durable backend plaintext queue" and gives deterministic failure states. -
Encrypted backend queue for later desktop replay - 🎯 7 🛡️ 9 🧠 9 - approx
3500-8000changed LOC. Better reliability, but bigger system. Backend still sees plaintext transiently from Telegram before encrypting. -
Non-2xx webhook until desktop online, using Telegram retries as queue - 🎯 4 🛡️ 4 🧠 4 - approx
800-2000changed LOC. Not recommended. It is brittle, provider-controlled, and creates operational backlog.
14. Decision Update
Official shared bot MVP should implement:
Telegram webhook
-> backend redaction-safe receipt
-> if desktop active: synchronous durable desktop claim
-> if accepted: ACK Telegram
-> if not accepted: offline notice policy, then ACK Telegram
Own bot mode remains:
desktop long polling
-> local durable turn
-> advance update offset
This keeps the default UX simple while making the privacy/reliability tradeoff explicit instead of accidental.