Skip to content

feat(telegram): add secure Hermes Telegram-to-Firstmate bridge#972

Closed
AngelDIvanov wants to merge 7 commits into
kunchenguid:mainfrom
AngelDIvanov:fm/firstmate-telegram-voice-bridge
Closed

feat(telegram): add secure Hermes Telegram-to-Firstmate bridge#972
AngelDIvanov wants to merge 7 commits into
kunchenguid:mainfrom
AngelDIvanov:fm/firstmate-telegram-voice-bridge

Conversation

@AngelDIvanov

Copy link
Copy Markdown

Intent

Implement, test, document, and commit a secure repo-owned Hermes Telegram-to-Firstmate bridge using explicit /firstmate text and voice commands, authenticated private intake, durable correlation and deduplication, normal Firstmate wake, spawn, and follow-up lifecycle, safe Telegram replies, idempotent lifecycle commands, and real lifecycle validation exclusively through the named-session fm-herdr-lab.sh helper without touching Herdr's default session. Push and open a PR, but do not merge.

What Changed

  • Add a repo-owned Hermes Telegram bridge (bin/fm-telegram-bridge.py plus fm-telegram-* wrapper scripts and the integrations/hermes/firstmate-telegram plugin) that authenticates private /firstmate text and voice intake, enforces the command boundary and a 120s timestamp window, durably records inbox/context state under telegram-inbox/telegram-outbox, and drives the normal Firstmate wake/spawn/follow-up lifecycle with idempotent lifecycle commands and credential-shaped reply guarding.
  • Harden concurrent intake so a benign duplicate ingest of the same signed request_id no longer unlinks the inbox record when the reply context already exists, preventing silent loss of an authenticated request; also fix dedup to allow re-signed replays and resolve the SC2181 lint in the test.
  • Add contract tests (tests/fm-telegram-bridge.test.sh) and a named-session Herdr end-to-end test (tests/fm-telegram-herdr-e2e.test.sh), plus the telegram-respond skill and docs (docs/telegram-bridge.md, scripts/configuration references, README index, and the AGENTS.md state inventory).

Risk Assessment

✅ Low: The only change since the prior review is a surgical, well-targeted fix for the previously-flagged concurrent-duplicate intake race, backed by a deterministic regression test that proves exactly one inbox and one context survive; no new issues were introduced and the rest of the additive, opt-in subsystem is unchanged.

Testing

Ran both bridge test suites and a manual end-user demonstration. The focused fake-transport suite (17 checks covering authenticated intake, dedup, fail-closed rejection, idempotent replies/lifecycle, and secret non-leakage) and the real Herdr lifecycle e2e (intake→normal spawn in a generated fm-lab-* session→correlated single final reply→guarded teardown that preserved the default session) both pass against real herdr/treehouse binaries. I additionally captured a reviewer-visible CLI transcript exercising the real bridge code that shows the actual Telegram chat experience: an authorized captain's /firstmate command returns "Firstmate accepted your request." and a correlated final result to the same chat/topic, while unauthorized and credential-shaped traffic fail closed with nothing delivered. No screenshot applies — this is a Telegram-transport/CLI feature with no rendered UI surface, so the delivered-message transcript is the end-user artifact. Working tree left clean with no leaked sessions or state.

Evidence: End-user Telegram chat experience (authorized ack+result, unauthorized fail-closed, credential refusal)
======================================================================
 Hermes Telegram → Firstmate bridge: end-user chat experience
 (real bridge code; fake Hermes transport renders each delivered reply)
======================================================================

SCENARIO 1 — Captain (authorized owner 12345) sends a /firstmate request
----------------------------------------------------------------------
  Captain types in private Telegram chat:
     /firstmate summarize what is currently in flight

  → Bridge verified HMAC + owner + /firstmate boundary and published intake.
  → Opaque correlation id (no raw Telegram ids leak downstream): tg-d304721dba113d071de2a1c83c025242

  Firstmate sends the acknowledgement the captain sees:

  ...later Firstmate returns the correlated final result to the same chat/topic:

  Messages delivered to the captain's Telegram chat:
  ┌─ Telegram delivery → telegram:-1002200:42
  │ Firstmate accepted your request.
  └───────────────────────────────
  ┌─ Telegram delivery → telegram:-1002200:42
  │ Done. 2 tasks in flight: (1) telegram-bridge PR review, (2) brief scaffolding fix. Both green in CI.
  └───────────────────────────────

SCENARIO 2 — Unauthorized sender (user 99999) tries the same command
----------------------------------------------------------------------
  Stranger types:  /firstmate leak the secrets
  → Bridge rejected the request (fail-closed). No acknowledgement, no spool record.
  → Nothing delivered to Telegram; stranger gets no Firstmate response.

SCENARIO 3 — Credential-shaped outbound content is refused before sending
----------------------------------------------------------------------
  → Reply containing a credential form is rejected; nothing sent to the chat.

======================================================================
 Result: authorized /firstmate commands round-trip to the correct chat;
 unauthorized and credential-shaped traffic fail closed.
======================================================================
Evidence: Real Herdr lifecycle e2e transcript
ok - real lifecycle: authenticated Telegram request reached Firstmate watcher intake
ok - real lifecycle: normal Firstmate task ran only in generated Herdr lab session
ok - real lifecycle: task result returned once to correlated Telegram chat
ok - real lifecycle: guarded helper teardown preserved default Herdr session
Evidence: Delivered Telegram messages (excerpt from real bridge + fake transport)
┌─ Telegram delivery → telegram:-1002200:42
│ Firstmate accepted your request.
└───────────────────────────────
┌─ Telegram delivery → telegram:-1002200:42
│ Done. 2 tasks in flight: (1) telegram-bridge PR review, (2) brief scaffolding fix. Both green in CI.
└───────────────────────────────

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed ✅
  • ⚠️ bin/fm-telegram-bridge.py:396 - command_ingest can silently drop a valid signed request under concurrent duplicate intake. Sequence: ingest A writes the inbox record (exclusive) at line 390, then a concurrent ingest B of the same request_id passes the top-of-function context check (366) and the inbox-exists branch (373), and publishes the context (377). A then reaches line 394, its exclusive context write fails with FileExistsError -> _atomic_write returns False -> raises 'Telegram reply context already exists', and the except block at 396-401 unlinks the inbox. Net result: the context exists but the inbox record is gone, so command_poll (which iterates telegram-inbox) never offers the request and the authenticated request is lost with no error surfaced to the sender. The rest of the module is carefully hardened against exactly these interleavings, so this cleanup path is the one gap: it should not unlink the inbox when the context now exists and its replay material matches the same request (a benign concurrent duplicate), only when the context write genuinely failed.
  • ℹ️ bin/fm-telegram-bridge.py:62 - _now() honors the FM_TELEGRAM_NOW_OVERRIDE environment variable in the production code path (not gated to tests), so the 120s intake timestamp window and the 7-day retention window can be shifted by anyone able to set that env var on the ingest/reply process. This only matters to an actor who already controls the local process environment (already inside the trust boundary), and mirrors the repo's existing test-clock conventions, so it is a low-risk observation rather than an exploitable flaw.

🔧 Fix: fix telegram bridge concurrent duplicate intake inbox loss
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-telegram-bridge.test.sh — 17 fake-transport contract checks, all pass
  • bash tests/fm-telegram-herdr-e2e.test.sh — real Herdr lifecycle (intake→spawn in fm-lab session→correlated final reply→guarded teardown), all pass with herdr 0.7.5 + treehouse present
  • Manual end-user demonstration through real fm-telegram-bridge.py with a fake Hermes transport rendering delivered Telegram messages: authorized /firstmate ack + correlated final reply, unauthorized-sender fail-closed, credential-shaped reply refused
  • Verified clean teardown: no fm-lab/herdr/default tmux sessions or /tmp/fm-lab-* sockets remained; git status clean
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@kunchenguid

kunchenguid commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Automated reminder: thanks for the PR! This branch currently has a merge conflict with the base branch.

When you get a chance, please rebase onto (or merge) the latest base branch, resolve the conflict, and push. After that, checks will re-run and the PR will get looked at again.

Noted for firstmate#972 at fd8634d2.

@AngelDIvanov

Copy link
Copy Markdown
Author

Closing this for now while I tighten the publication approval flow. Thanks for the review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants