Skip to content

fix(bluebubbles): send-only standalone delivery collides with live webhook - #100225

Closed
krullma wants to merge 1 commit into
NousResearch:mainfrom
krullma:fix/bluebubbles-sendonly-webhook
Closed

krullma wants to merge 1 commit into
NousResearch:mainfrom
krullma:fix/bluebubbles-sendonly-webhook

Conversation

@krullma

@krullma krullma commented Sep 1, 2026

Copy link
Copy Markdown

Summary

Cron/standalone delivery builds a one-shot BlueBubblesAdapter to POST a message while the gateway live adapter already holds the webhook listener. Three failure modes, all reproduced on a real BB deployment (macOS, gateway + BB server on same host):

  1. EADDRINUSE on every fallback sendconnect() unconditionally binds webhook_host:webhook_port; a second bind on 8645 fails with [Errno 48] address already in use, killing the send before it runs. Cron jobs that hit the standalone fallback silently delivered nothing.
  2. disconnect() wipes the live webhook registration — the one-shot adapter unregisters its webhook URL, which is byte-identical to the live adapter's. BlueBubbles stops receiving inbound events; outbound REST keeps working so nothing looks broken. Inbound iMessage dies silently.
  3. Registration URL rewritten to localhost_webhook_url normalizes 127.0.0.1localhost; the listener binds IPv4 only and macOS resolves localhost to ::1 (IPv6) first, so BlueBubbles POSTs events at a dead port. Inbound dies again, differently.

Fix

  • Add connect_send_only (from platform extra or BLUEBUBBLES_CONNECT_SEND_ONLY): connect() pings the server then skips the webhook bind/register; disconnect() skips the unregister.
  • tools/send_message_tool._send_bluebubbles sets the flag (it builds a one-shot adapter by design).
  • _webhook_url keeps the literal 127.0.0.1 instead of rewriting to localhost.

Test Plan

  • 28 tests pass in tests/gateway/test_bluebubbles.py (24 existing + 4 new: send-only flag from extra/env, default off, and send-only disconnect must not call _unregister_webhook; webhook URL tests updated to assert 127.0.0.1 survives normalization).
  • Live E2E on macOS: standalone send via _send_bluebubbles succeeds (SendResult.success=True), no second bind on 8645, webhook registration intact after disconnect, and inbound iMessage events reach the gateway again (verified by sending a reply from the phone).

…bhook

Cron/standalone delivery builds a one-shot BlueBubblesAdapter to POST a
message while the gateway's live adapter already listens on the webhook
port. Three failure modes, all reproduced on a real BB deployment:

1. connect() unconditionally binds webhook_host:webhook_port →
   EADDRINUSE kills the fallback send before send() runs (cron jobs
   'delivered' nothing for days).
2. The one-shot disconnect() unregisters the webhook by URL — identical
   to the live adapter's registration — wiping it from BlueBubbles.
   Outbound REST keeps working, inbound iMessage events silently die.
3. _webhook_url normalises 127.0.0.1 → localhost; the listener binds
   IPv4 only and macOS resolves localhost to ::1 first, so the server
   POSTs events at a dead port. Inbound dies again, differently.

Fix: add connect_send_only (platform extra or BLUEBUBBLES_CONNECT_SEND_ONLY)
— connect() pings the server, skips the webhook bind/register, and
disconnect() skips the unregister. The standalone send path in
send_message_tool sets the flag; _webhook_url now keeps the literal
127.0.0.1 instead of rewriting to localhost.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery 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 duplicate This issue or pull request already exists labels Sep 1, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Duplicate of #51763 for the standalone send-only lifecycle: it already avoids the webhook bind/register/unregister collision. This branch also carries loopback normalization work from the existing BlueBubbles repair family; please split or rebase any distinct portion.

@krullma

krullma commented Sep 1, 2026

Copy link
Copy Markdown
Author

Agreed — closing this as duplicate. #51763 covers the send-only bind/unregister lifecycle (same root cause, cleaner API via connect(send_only=True)), and #97672 covers the loopback normalization.

One thing worth adding for the other two PRs, from a real deployment (macOS, gateway + BlueBubbles server on the same host, cron delivery + inbound iMessage):

Both were reproduced and fixed locally; happy to help review either PR.

Also: the send-only flag currently on my branch reads from platform extra/env; #51763's signature-based approach is strictly better.

@krullma krullma closed this Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants