fix(bluebubbles): use 127.0.0.1 in webhook URL + migrate stale localhost registrations - #69593
Conversation
a6c7a03 to
ae4538d
Compare
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing the default IPv4/localhost mismatch; current main still has that mismatch: gateway/platforms/bluebubbles.py:284 binds self.webhook_host, while :315-318 registers localhost for the default 127.0.0.1 configuration.
Problems
- The changed host set includes
::, but the listener still binds the configured host atgateway/platforms/bluebubbles.py:284. Rewriting that IPv6 bind to127.0.0.1registers an address with no corresponding IPv4 listener. The changed parameterized test accepts this broken mapping rather than exercising the bind/advertise contract. - The migration only queries
localhost?...password=<current password>. Commit326cbbe40eaintroduced password-bearing registrations after prior releases used bare URLs; exact matching ingateway/platforms/bluebubbles.py:343-349leaves those older stale registrations untouched.
Suggested changes
- Keep registration address family consistent with the
TCPSitebind, including::. - Migrate both bare and password-bearing historical localhost registrations, with a regression test for the pre-
326cbbe40eaform.
Automated hermes-sweeper review.
…ost registrations On macOS, Node.js resolves 'localhost' to IPv6 ::1 first, but the aiohttp webhook listener binds IPv4 127.0.0.1 only. Normalise all loopback hosts to the explicit IPv4 literal so webhook deliveries succeed instead of silently failing with ECONNREFUSED ::1. Also adds automatic migration of stale 'localhost'-based registrations left by prior Hermes versions. Without cleanup, the old registration persists as a dead entry because _unregister_webhook only removes the current URL. The new _register_webhook detects and removes legacy localhost registrations before creating the new one, providing a seamless upgrade path. Fixes NousResearch#8512, covers NousResearch#45308.
|
Thanks for the review — both points are addressed and the branch is rebased onto current 1. Bind / advertise address-family consistency
Added 2. Legacy localhost migration (bare + password-bearing)Migration now always tries the bare Added Verify
|
ae4538d to
312d394
Compare
|
Friendly ping — it's been about a week since this was rebased and the review feedback was addressed (bind/advertise address-family consistency + bare/password-bearing legacy localhost migration, both with regression tests). The branch is still mergeable against current main. This one is a real daily-driver bug for macOS BlueBubbles users: without it, inbound webhooks silently fail whenever the listener binds IPv4 but the registered URL uses localhost (which Node resolves to ::1 first). No rush, but a re-review when someone has a moment would be appreciated. Happy to make any further changes. |
|
Additional data point on why the IPv4 literal is required on macOS — tested 2026-08-07 on macOS (Python 3.11.15, aiohttp 3.14.1):
Practical consequence: the docs' default behavior (register |
What does this PR do?
Fixes the BlueBubbles inbound webhook silently failing on macOS, and migrates stale
localhostregistrations left by prior Hermes versions.The bug
The adapter registered its inbound webhook as
http://localhost:<port>/..., but the aiohttp listener binds IPv4127.0.0.1only. On macOS,localhostresolves to IPv6::1first, so BlueBubbles (Node.js) POSTs the webhook to::1and the delivery is silently dropped — no error surfaces, but Hermes never receives inbound messages.The fix
Normalise all loopback/wildcard hosts (
0.0.0.0,127.0.0.1,localhost,::) to the explicit IPv4 literal127.0.0.1so the registered URL matches the address the listener actually binds.The migration (addresses hermes-sweeper feedback on #8263)
Existing installations that already have a
localhost-based webhook registered with BlueBubbles won't get cleaned up by_unregister_webhook— it only removes the current URL. The stalelocalhostregistration survives as a dead entry, and BlueBubbles may still attempt to deliver to the broken URL.This PR adds:
_legacy_webhook_urlsproperty: computes the pre-fixlocalhostvariant of the current URL_register_webhook: before creating the new registration, finds and removes any stalelocalhost-based registrations left by earlier versionsThis is the same core fix as #8263, but with the sweeper-requested upgrade-path migration included.
Related Issue
Fixes #8512 (BlueBubbles webhook fails on macOS due to IPv6 localhost resolution).
Covers #45308.
Type of Change
Changes Made
gateway/platforms/bluebubbles.py:_webhook_url: normalises loopback hosts to127.0.0.1instead oflocalhost_legacy_webhook_urls: new property returning pre-fix URL variants for migration_register_webhook: removes stalelocalhostregistrations before creating the new onetests/gateway/test_bluebubbles.py:TestBlueBubblesWebhookUrlto assert IPv4 normalisationtest_legacy_webhook_urls_returns_localhost_varianttest_legacy_webhook_urls_empty_when_custom_hosttest_register_migrates_legacy_localhost_webhooktest_register_skips_migration_when_no_legacyHow to Test
::1, refused by the IPv4-only listener).curl -s -o /dev/null -w '%{http_code}' http://[::1]:<port>/...→000(connection refused, confirms IPv6 path dead) andcurl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:<port>/...→200. Inbound iMessage now reaches Hermes and it replies.localhostregistration. Start the new gateway — the stale registration is automatically removed and replaced with the127.0.0.1one.pytest tests/gateway/test_bluebubbles.py -q→ 64 passed.Checklist
fix(scope):)_webhook_urlnormalizes 127.0.0.1 → localhost, breaking IPv4 webhook delivery (inbound attachments dropped) #45308, fix(bluebubbles): use 127.0.0.1 instead of localhost for webhook URL #8263)pytest tests/gateway/test_bluebubbles.py -q— 64 passedNote for reviewers
This incorporates the core fix from #8263 (normalise to
127.0.0.1) plus the stale-localhost migration that the hermes-sweeper flagged as a blocker on that PR. An alternative approach is to make the listener dual-stack (bindNone) and keeplocalhost; this minimal change keeps the IPv4 listener and pins the URL, which is lower risk. Happy to switch to dual-stack if preferred.