Skip to content

fix(bluebubbles): repair same-host webhook flow and iMessage sanitization - #9272

Open
trevorgordon981 wants to merge 1 commit into
NousResearch:mainfrom
trevorgordon981:fix/bluebubbles-same-host
Open

fix(bluebubbles): repair same-host webhook flow and iMessage sanitization#9272
trevorgordon981 wants to merge 1 commit into
NousResearch:mainfrom
trevorgordon981:fix/bluebubbles-same-host

Conversation

@trevorgordon981

ghost commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Summary

What's in this PR

All four fixes live in gateway/platforms/bluebubbles.py:

  1. Drop invalid "message" event from the webhook registration payload (fixes BlueBubbles adapter registers invalid "message" event; 400 Bad Request blocks all webhook registration #9263). BlueBubbles Server only accepts new-message / updated-message (among others) and 400s the entire POST /api/v1/webhook if "message" is in the list — so on stock main, no events get subscribed at all.

  2. Embed the password in _webhook_url (fixes BlueBubbles adapter webhook URL omits password; every dispatch 401s and no inbound messages reach the agent #9265). BlueBubbles WebhookService.dispatchEvent() has no auth-header configuration, so every dispatch was being rejected by _handle_webhook's password check with 401 Unauthorized.

  3. Use the 127.0.0.1 literal in _webhook_url instead of rewriting loopback hosts to "localhost". On macOS with modern Node.js, BlueBubbles Server resolves localhost::1 (IPv6 first), but the adapter's aiohttp.web.TCPSite binds IPv4-only, so every same-host dispatch fails with ECONNREFUSED ::1:8645. Switching to the literal avoids the AAAA lookup entirely.

  4. Strip iMessage tofu characters in outbound sends. Apple Color Emoji has no glyphs for two ranges that routinely leak through the pipeline:

    • Block Elements (U+2580..U+259F) — streaming-cursor artifacts like that the agent's stream renderer can leave in the final message.
    • Private Use Area (U+E000..U+F8FF plus the two supplementary PUAs) — Slack custom workspace emojis are stored here and get carried through whenever Slack history / SOUL / memory feeds back into model context.
      A new _sanitize_for_imessage() helper is called from send() and format_message() before the outbound REST call so the user sees clean text instead of boxes.

#1 and #2 each independently prevent any inbound message from reaching the agent, and #3 prevents anything if you run BB Server and Hermes on the same Mac (which is the documented recommended setup). In combination, the native BlueBubbles adapter has not worked out of the box for same-host installs since it was added. #4 is a quality-of-life fix that becomes visible as soon as the first three are in place.

Test plan

  • With the "message" event removed, _register_webhook returns status=200 against a real BlueBubbles Server 1.9.9 and GET /api/v1/webhook shows the new entry with events: ["new-message", "updated-message"].
  • With the password embedded in _webhook_url, inbound iMessages from BlueBubbles dispatch to Hermes and hit _handle_webhook as 200 OK instead of 401; the agent actually runs and produces a reply.
  • With the host rewritten to 127.0.0.1, aiohttp.TCPSite bind and BB dispatch target are both unambiguously IPv4; same-host dispatch succeeds where it previously returned ECONNREFUSED ::1:8645 in ~/Library/Logs/bluebubbles-server/main.log.
  • _sanitize_for_imessage unit-sanity tested against "Hello ▉", "Done ▉", strings with \uE001\uE002 PUA chars, strings with legitimate 🙂 ✅ emoji (preserved), and mixed strings. Trailing-whitespace cleanup works as expected.
  • End-to-end: an iMessage sent from a real user handle to the BlueBubbles Mac reaches the Hermes agent, the agent replies via the adapter's outbound send path, and the reply appears in Messages without any tofu characters.

Notes

🤖 Generated with Claude Code

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for preserving the remaining same-host URL fix and the outbound rendering cleanup. The webhook-event and password-registration parts described in the PR body are already on current main (064f8d74d, 326cbbe40), while the default-host mismatch remains: gateway/platforms/bluebubbles.py:283 binds self.webhook_host, but :315-316 rewrites default 127.0.0.1 to localhost.

Problems

  • gateway/platforms/bluebubbles.py:77-80 adds iMessage sanitization, but the PR test diff only updates _webhook_url assertions. There is no regression test for Block Elements, BMP/supplementary PUA removal, preserved ordinary emoji, or whitespace cleanup.

Suggested changes

  • Add focused format_message() or helper tests for those sanitization contracts.
  • Salvage only the still-needed URL normalization and sanitization portions; the webhook-event and password fixes are already implemented on main at gateway/platforms/bluebubbles.py:320-331 and :374-377.

This is an automated hermes-sweeper review.

# * Block Elements (U+2580..U+259F) — streaming-cursor artifacts like ▉ that
# the agent's stream renderer can leak into the final message.
# * Private Use Area (U+E000..U+F8FF) — Slack custom workspace emoji live
# here and get carried through when Slack history ends up in model context.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add regression coverage for this helper via format_message(): Block Elements, BMP and supplementary PUA code points, preserved standard emoji, mixed text, and trailing whitespace. The current test changes cover only URL normalization.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 12, 2026
Adds regression coverage for the iMessage tofu-character sanitization
(Block Elements U+2580..U+259F, BMP PUA U+E000..U+F8FF, supplementary
PUAs U+F0000..U+FFFFD / U+100000..U+10FFFD), confirming ordinary emoji
are preserved and trailing whitespace is rstripped. Also updates the
_webhook_url assertions for the new 127.0.0.1 loopback normalization
(including the ::1 host) and wires _sanitize_for_imessage into
format_message().
@trevorgordon981
trevorgordon981 force-pushed the fix/bluebubbles-same-host branch from 39faa12 to fd70bae Compare August 7, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Assignees

Couldn't load assignees.