Skip to content

Require a passphrase on texts to Hermes so a spoofed number can't reach it - #65

Draft
windoze95 wants to merge 1 commit into
mainfrom
claude/tender-shannon-b4wkin
Draft

windoze95 wants to merge 1 commit into
mainfrom
claude/tender-shannon-b4wkin

Conversation

@windoze95

Copy link
Copy Markdown
Owner

Closes #21.

Owner passthrough forwarded any text whose sender matched the owner's number to the full Hermes agent. SMS caller ID can be spoofed, so anyone faking that number could use Hermes and its tools.

What changes

  • Passphrase (hermes.passphrase, optional, 6–64 characters). With one set, only texts from the owner's number that start with it are relayed, for example otter-42 what's on today?. Case is ignored (keyboards capitalize the first word), and a space, colon or comma must follow it, so otter-420 … doesn't match. Hermes gets the text without the passphrase. Other texts stay in Messages with the usual generic notification. A text that is only the passphrase asks nothing and isn't relayed.
  • Guessing is limited. Five texts from the owner's number without the passphrase within an hour pause the relay, even for correct texts, until fewer than five fall within the past hour. This keeps a short passphrase from being guessed by spoofed texts. Misses and pauses are written to the on-device event log (content-free) and shown as the Hermes screen's last-text status.
  • Changes revoke queued work. At receipt, a queued owner text records whether a passphrase was required. Before asking Hermes and before replying, the text must still match the current passphrase and requirement, like the existing owner and connection binding. Changing, setting or clearing the passphrase therefore cancels queued texts and replies.
  • Hermes never sees it. GET /v1/texts (recent_texts, direct and hub-backed) returns the owner's incoming texts without the passphrase. Otherwise Hermes, or a prompt injection in someone else's text, could read it back out of the owner's conversation.
  • Settings screen. Hermes → Your texts to Hermes has a passphrase field (visible-password input, so the keyboard doesn't learn it) and a note explaining the trade-off: with a passphrase, only texts that start with it are relayed; without one, a faked text could use Hermes and its tools.
  • Config. The passphrase round-trips through ConfigJson and can be pushed with the adb config receiver on a direct R1. As with every other hermes.* key, a paired R1 refuses it over adb.
  • Docs. HERMES.md, HERMES_DIRECT.md (including Security notes), MESSAGES.md, and release notes in docs/releases/v0.6.10.md.

The passphrase is optional: existing relays keep working until one is set, and the settings note spells out the risk. Two other options from the issue aren't in this PR. An hourly cap on relayed texts mostly matters once a passphrase has leaked. A narrower Hermes toolset for the SMS session is configured on the Hermes host.

Tests

  • HermesTextsTest:
    • texts without the passphrase stay local, and Hermes gets the stripped text;
    • five misses pause the relay, which resumes after an hour, and the passphrase alone isn't a miss;
    • changing, setting or clearing the passphrase cancels a queued reply;
    • prefix, separator and redaction edge cases.
  • DeviceApiTest: /v1/texts leaves the passphrase out of the owner's texts, in both the thread and the conversation list, and leaves others' texts unchanged.
  • HermesActivityTest: the screen rejects a short passphrase, trims, saves, prefills and clears it.
  • ConfigJsonTest: round trip, merge preservation, clearing, and rejection of short, padded, multi-line or over-long values.
  • ConfigReceiverTest: a paired R1 refuses passphrase over adb.

Locally: :app:testDebugUnitTest passed (399 tests, 0 failures), and :app:assembleDebug and :app:lintDebug succeeded. Not yet tried on an R1 with real SMS.

🤖 Generated with Claude Code

https://claude.ai/code/session_014p2tHgu89Vj7BouaizkGX7


Generated by Claude Code

…ch it (#21)

SMS sender numbers can be spoofed, so a stranger faking the owner's
number could use Hermes and its tools through owner passthrough.

- hermes.passphrase (6–64 characters, optional): with it set, only texts
  from the owner's number that start with it are relayed. Case is
  ignored, a space, colon or comma must follow, and Hermes gets the text
  without it. Other texts stay in Messages.
- Five texts from the owner's number without it within an hour pause
  the relay, so a short passphrase can't be guessed by texting. Misses
  and pauses go to the on-device event log and the Hermes screen.
- Queued owner texts are bound to the passphrase requirement at receipt;
  changing or clearing the passphrase cancels them, like an owner or
  connection change.
- The device API's /v1/texts (recent_texts) leaves the passphrase out of
  the owner's texts, so Hermes never sees it.
- Hermes → Your texts to Hermes has the passphrase field and explains
  the trade-off. Docs and release notes for 0.6.10.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014p2tHgu89Vj7BouaizkGX7

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Protect texting Hermes against a spoofed sender number

2 participants