Summary
The bundled iMessage plugin runs two separate group allowlist gates back-to-back in extensions/imessage/src/monitor/inbound-processing.ts:
- Sender / chat-target allowlist —
isAllowedIMessageSender against channels.imessage.groupAllowFrom. Matches by handle, chat_guid, chat_identifier, or chat_id.
- Group registry —
resolveChannelGroupPolicy at line 199. With groupPolicy: \"allowlist\":
allowlistEnabled = true
allowAll requires channels.imessage.groups[\"*\"] to exist
- Without that wildcard (or an explicit per-
chat_id entry), allowed = false → drop at line 336-340
The drop is logged via params.logVerbose?.(...) at line 337-339:
if (isGroup && groupListPolicy.allowlistEnabled && !groupListPolicy.allowed) {
params.logVerbose?.(
`imessage: skipping group message (${groupId ?? \"unknown\"}) not in allowlist`,
);
return { kind: \"drop\", reason: \"group id not in allowlist\" };
}
logVerbose only fires at logging.level: debug or finer. At the default info level, every group message is silently dropped with no log entry, no error response to the sender, and no telemetry.
Why it matters / who it bites
This is the most common BlueBubbles → bundled-iMessage migration failure mode (PR #78317 documents it). BlueBubbles operators copy groupAllowFrom and groupPolicy but skip the groups block, because BB's typical groups: { \"*\": { \"requireMention\": true } } looks like an unrelated mention setting — it's actually load-bearing for gate 2. After the cutover, group messages vanish silently. DMs continue to work because they take a different code path. Operators have no signal that anything is wrong unless they think to raise log level.
Reported by Omar Shahine on 2026-05-06 after live debugging revealed every group message Lobster received over iMessage since the migration had been silently dropped at gate 2.
Proposed fix (sketch — happy to iterate)
Either or both:
-
Raise the drop's log severity from verbose to warn (or info). A drop that's invisible at the default log level is user-hostile. The drop is rate-limited per-message, so log volume is bounded by inbound message rate, which is already paced. A warn line per dropped group message is a strong "your config is incomplete" signal.
-
Surface the misconfiguration at gateway start, not at message time. When groupPolicy === \"allowlist\" AND groups is missing/empty AND there are no per-chat_id entries, the gateway could log a one-time warn at startup like: \"channels.imessage.groupPolicy=allowlist with no channels.imessage.groups entries — every group message will be dropped. Add 'groups': { '*': { ... } } to allow all groups, or explicit chat_id entries.\". This catches the misconfiguration before any message lands.
-
Optional: doctor check that catches the same condition during openclaw doctor.
Out of scope
- Not changing the gate semantics — operators who genuinely want the strict allowlist behavior should keep it. This is purely about making the drop observable.
Tracking
Filed off the back of PR #78317. Documented as a footgun in docs/channels/imessage-from-bluebubbles.md ("Group registry footgun" section) and docs/channels/imessage.md (Group policy Warning block) in that same PR. This issue is for the underlying behavior change.
Summary
The bundled iMessage plugin runs two separate group allowlist gates back-to-back in
extensions/imessage/src/monitor/inbound-processing.ts:isAllowedIMessageSenderagainstchannels.imessage.groupAllowFrom. Matches by handle,chat_guid,chat_identifier, orchat_id.resolveChannelGroupPolicyat line 199. WithgroupPolicy: \"allowlist\":allowlistEnabled = trueallowAllrequireschannels.imessage.groups[\"*\"]to existchat_identry),allowed = false→ drop at line 336-340The drop is logged via
params.logVerbose?.(...)at line 337-339:logVerboseonly fires atlogging.level: debugor finer. At the defaultinfolevel, every group message is silently dropped with no log entry, no error response to the sender, and no telemetry.Why it matters / who it bites
This is the most common BlueBubbles → bundled-iMessage migration failure mode (PR #78317 documents it). BlueBubbles operators copy
groupAllowFromandgroupPolicybut skip thegroupsblock, because BB's typicalgroups: { \"*\": { \"requireMention\": true } }looks like an unrelated mention setting — it's actually load-bearing for gate 2. After the cutover, group messages vanish silently. DMs continue to work because they take a different code path. Operators have no signal that anything is wrong unless they think to raise log level.Reported by Omar Shahine on 2026-05-06 after live debugging revealed every group message Lobster received over iMessage since the migration had been silently dropped at gate 2.
Proposed fix (sketch — happy to iterate)
Either or both:
Raise the drop's log severity from
verbosetowarn(orinfo). A drop that's invisible at the default log level is user-hostile. The drop is rate-limited per-message, so log volume is bounded by inbound message rate, which is already paced. Awarnline per dropped group message is a strong "your config is incomplete" signal.Surface the misconfiguration at gateway start, not at message time. When
groupPolicy === \"allowlist\"ANDgroupsis missing/empty AND there are no per-chat_identries, the gateway could log a one-timewarnat startup like:\"channels.imessage.groupPolicy=allowlist with no channels.imessage.groups entries — every group message will be dropped. Add 'groups': { '*': { ... } } to allow all groups, or explicit chat_id entries.\". This catches the misconfiguration before any message lands.Optional: doctor check that catches the same condition during
openclaw doctor.Out of scope
Tracking
Filed off the back of PR #78317. Documented as a footgun in
docs/channels/imessage-from-bluebubbles.md("Group registry footgun" section) anddocs/channels/imessage.md(Group policy Warning block) in that same PR. This issue is for the underlying behavior change.