Telegram functionality is split into a provider-neutral channel policy core, Telegram-specific pure contracts, the live BFF composition, member/admin/web adapters, the Mini App, and deployment. Keeping those layers distinct prevents a pure planner or demo surface from being mistaken for a deployed effect.
Component map#
Channel registry and dispatcher#
The global registry defines telegram-bot, telegram-miniapp, and
telegram-channel with capability, cost, residency, consent, retention,
disclosure, and crisis metadata. The dispatcher maps typed intents and content
variants to a delivery/suppression decision after tier, crisis, quiet-hour,
frequency, tenant, opt-in, residency, content-class, and capability checks.
The boundary layer defines allowed content classes, disclosure mechanism, provenance preservation, audit event names, and durable-memory ingestion/ redaction. It is pure policy; it does not call Telegram.
See Channel abstraction, routing, and boundaries.
Shared Telegram library#
libs/oshun/messaging-channels/src/telegram owns:
| Module family | Responsibility |
|---|---|
bot, room-commands, rooms |
Update and command types/handler, room reads/writes, crisis and response construction. |
command-surface, deep-links |
Private/group command registration, menu button, start payloads, discoverability. |
keyboard, bot-api-limits, deliver |
Inline buttons/callback data, Telegram constraints, response-to-Bot-API translation and delivery. |
effects, reminders, reminder-requests |
Save/preferences/timezone/delivery/unlink ports and reminder parsing/lifecycle. |
sophia-grounder, rendering |
Retrieval/grounding adapter and disclosure/provenance rendering. |
stt-provider, tts-provider, presence behavior |
Voice download/transcription/synthesis seams and chat actions. |
security |
Login Widget/initData verification, scoped Mini App sessions, sensitive-action classification. |
inline, publishing |
Inline results/abuse and editorial-channel post/takedown models. |
upgrade, payments |
Upgrade port and Telegram invoice/payment lifecycle models. |
rate-limit, webhook-registration |
Abuse/rate port and operational webhook/username description/verdict. |
These modules prefer pure functions and typed ports. Provider I/O enters through an injected fetch/adapter or the BFF composition.
Live BFF webhook#
The supported live inbound route under apps/oshun/bff/src/routes/telegram.ts
performs request/secret verification, update parsing, durable claim/dedupe,
runtime composition, handler invocation, Bot API delivery, provider-result
inspection, and update outcome. BFF modules compose:
- Sophia retrieval/grounding bridge;
- rooms and reminders adapters;
- save/preference/timezone/unlink effects;
- member local time/zone;
- upgrade/payment bridge;
- Mini App surface/security state;
- durable user/binding/update stores;
- command-surface and webhook registration;
- scheduled reminder/outbound delivery cycle.
The webhook can reply to an inbound update only after effect and safety policy agree. It does not treat generating response JSON as delivery.
Standalone bot package#
apps/oshun/telegram-bot contains a long-poll-capable composition and tests,
but its README explicitly says it is not a deployed service. It must remain a
thin client of BFF-owned durable APIs if used; process-local room/reminder state
would diverge from web/BFF state. Do not deploy it beside the webhook with both
consuming the same bot updates.
Mini App#
apps/oshun/telegram-miniapp is a Next.js application with landing and dynamic
surface routes, Telegram theme/WebApp bridge, Mini App client, curated surface
data/renderers, unit/component tests, and Playwright coverage. The BFF owns
initData verification, session issuance, allowed scopes, CORS policy, and real
domain calls.
Static/view data and local status interactions are presentation; they become product effects only when a verified scoped call reaches a system of record. See Mini App, authentication, and account linking.
Web profile and authentication#
apps/oshun/web/src/auth/telegram owns the Login Widget integration.
apps/oshun/web/src/app/profile/telegram owns link status/control UI. BFF
binding storage enforces provider identity uniqueness and durable lifecycle. Web
E2E tests cover widget/profile bridge, binding, and delivery integration.
Admin channel registry#
apps/oshun/admin/src/app/messaging/telegram-channels loads and presents
Telegram editorial channel configuration/health for authorized operators. Its
actions must use the admin/session and channel registry boundary, keep bot
tokens out of the browser, distinguish configured from provider-verified, and
preserve publication/audit ownership.
Core persisted records#
Exact schemas belong to their packages/migrations, but the architecture needs these logical identities:
| Record | Key invariants |
|---|---|
| Telegram binding | Stable Telegram user id ↔ Oshun member; uniqueness, verification source/time, consent/status, version, unlink/revocation, audit. |
| User/channel state | Member/chat delivery stop, voice/settings/timezone/quiet hours, version, last relevant provider state. |
| Update claim/dedupe | Bot/update id, kind, claim/lease, attempts, terminal outcome, expiry; unique enough to prevent repeated effects. |
| Reminder | Member/binding/chat, domain content/reference, local expression, resolved instant/zone, status, attempts/lease, delivery/action identity. |
| Delivery | Intent/content, chat, message/correlation/idempotency, provider calls/responses/message ids, partial/permanent/blocked state. |
| Mini App session/security state | Verified Telegram identity, Oshun binding, scopes, issue/expiry, audience/issuer, revocation/replay state. |
| Editorial channel/post | Tenant/channel identity, domain, review/schedule/version, provider message id, correction/takedown, delivery/audit. |
| Payment/upgrade | Member/order/plan/amount/currency/rail, invoice/payload/charge, pre-checkout/payment/refund, entitlement and reconciliation. |
Secrets and raw content are not part of every record. Data classification, retention, deletion, and redaction apply per field/purpose.
Idempotency and ordering#
Telegram retries webhooks and can deliver different update kinds around the same user action. The BFF claims updates durably and effects use stable idempotency. Callback, reminder, payment, and membership state machines validate current state/version; late, duplicated, edited, and reordered updates cannot repeat a save/payment/entitlement or restore stopped delivery accidentally.
Outbound sends have their own idempotency and receipt state. Telegram does not offer a universal send idempotency key, so Oshun must fence attempts and reconcile uncertain provider outcomes rather than blindly retrying.
Integration boundaries#
- Sophia: grounded retrieval/abstention and evidence/provenance; Telegram handles channel rendering, not truth production.
- Tara/Arete/Nyx/Nisaba: domains own room state/effects; Telegram owns the compact interaction and delivery.
- Iris/member identity: durable memory/consent/profile and binding context; Telegram transport data is minimized.
- Payments/entitlements: billing owns price/order/entitlement/refund; Telegram owns invoice/update transport where used.
- Veritas/editorial: editorial owner approves/version-controls content; Telegram channel adapter publishes and records delivery/takedown.
- V3 sessions: target product owns session/entitlement; Telegram carries a safe reminder/deep link.
Contract change checklist#
- Update the closed command/update/callback/surface/scope/content/status vocabulary and all exhaustive handlers.
- Regenerate or update clients/config where applicable.
- Migrate durable records with mixed-version compatibility and rollback/ forward-fix guidance.
- Verify private/group/channel/inline and member/tenant boundaries.
- Add adapter and webhook-to-real-effect/delivery tests; pure-planner coverage alone is insufficient.
- Update deployment tasks/settings/secrets, webhook/command registration, dashboards/alerts/runbooks, and this handbook.
- Re-run Docs Center generation and integrity/browser gates.