Skip to content

feat(whatsapp): notify the owner when a non-allowlisted sender messages - #53745

Open
marcelopaniza wants to merge 1 commit into
NousResearch:mainfrom
marcelopaniza:feat/whatsapp-stranger-knock
Open

feat(whatsapp): notify the owner when a non-allowlisted sender messages#53745
marcelopaniza wants to merge 1 commit into
NousResearch:mainfrom
marcelopaniza:feat/whatsapp-stranger-knock

Conversation

@marcelopaniza

Copy link
Copy Markdown

Problem

When a WhatsApp sender isn't on WHATSAPP_ALLOWED_USERS, the bridge drops the
message and only logs {"event":"ignored","reason":"allowlist_mismatch"}. The
owner has no idea anyone tried to reach them — a friend who isn't on the list
yet, a new contact for an event RSVP, etc. From the sender's side it just looks
like the bot ignored them.

What this adds

A "stranger knocking" notice with one-tap approval — without weakening the
allowlist (the stranger still can't talk to the agent):

scripts/whatsapp-bridge/bridge.js

  • At the allowlist_mismatch branch, emit a deduped knock event (sender id
    • number + a short, whitespace-collapsed, 140-char preview) into the existing
      message queue instead of only logging. De-dup is one notice per sender per 6h.
  • New POST /allow endpoint: normalizes a number, adds it to the live
    ALLOWED_USERS set, and appends it to allowlist-runtime.txt next to the
    session (loaded + merged at startup) — so approvals take effect immediately and
    survive a restart, with no env edit.

plugins/platforms/whatsapp/adapter.py

  • _poll_messages routes each item through _handle_special_inbound first:
    • a knock → send a notice to WHATSAPP_HOME_CHANNEL:
      🔔 <name> (+<num>) said: "<preview>" — reply allow <num> to let them in
    • the owner's allow <number> reply (home channel only) → POST /allow + confirm.

Security

  • The stranger's text is untrusted: it is only ever shown to the owner as a
    quoted preview and is never dispatched to the agent (the knock returns early).
  • Only the home channel can approve (allow <number> from any other sender is
    ignored).
  • No-op unless WHATSAPP_HOME_CHANNEL is set — deployments without a home
    channel keep silently dropping non-allowlisted senders, unchanged.

Testing

Verified on a live bot-mode deployment: a non-allowlisted number triggers exactly
one 🔔 notice to the home channel; allow <num> adds them (live, no restart)
and they can then converse; repeat messages from the same stranger are de-duped;
POST /allow returns {"success":true,"id":"..."} and writes the runtime file.

🤖 Generated with Claude Code

Non-allowlisted WhatsApp senders are silently dropped at the bridge, so the
owner never learns someone tried to reach them — a friend not yet on the list, a
new contact. It is invisible and easily mis-debugged as "the bot ignored me".

Surface it instead, with a one-tap approval path:

- bridge.js emits a deduped `knock` event (sender + a short, sanitized preview)
  into the message queue instead of only logging `allowlist_mismatch`, and gains
  POST /allow to add a number to the live allowlist at runtime (persisted to
  allowlist-runtime.txt next to the session and merged at startup — no restart).
- the adapter consumes `knock`: it sends a notice to WHATSAPP_HOME_CHANNEL
  ("<name> (+<num>) said: <preview> — reply `allow <num>`") and intercepts the
  owner's `allow <number>` reply (home channel only) to call POST /allow + confirm.

Security: the stranger's text is treated as untrusted — only ever shown to the
owner as a quoted preview, never dispatched to the agent. Only the home channel
can approve. Knock notices are a no-op unless WHATSAPP_HOME_CHANNEL is set, so
existing deployments keep silently dropping non-allowlisted senders.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alt-glitch alt-glitch added type/feature New feature or request comp/plugins Plugin system and bundled plugins platform/whatsapp WhatsApp Business adapter sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data P3 Low — cosmetic, nice to have and removed comp/plugins Plugin system and bundled plugins sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Jun 27, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related cluster: companion to your #53742 (per-sender toolset gating), and overlaps the broader WhatsApp authz PR #53623 and the four-tier-concierge RFC #16017. Distinct concern (stranger-knock notify), so not a duplicate — a human should pick how these compose.

@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 addressing an actual visibility gap: current main silently logs and drops denied non-allowlisted senders at scripts/whatsapp-bridge/bridge.js:637-647.

Problems

  • plugins/platforms/whatsapp/adapter.py:1203-1204 authorizes allow when the chat matches WHATSAPP_HOME_CHANNEL. The home channel is documented as a general notification/cron chat (plugins/platforms/whatsapp/plugin.yaml:26-29), so a group home channel lets any allowed group member approve a new number.
  • scripts/whatsapp-bridge/bridge.js:207-212 adds an unbounded knockSeen map; rejected messages from distinct senders accumulate for the process lifetime.
  • The diff adds no automated tests for the new authorization and persistent-allowlist paths. AGENTS.md:84-87 requires real-path validation for security boundaries and file/network I/O.

Suggested changes

  • Require a verified owner sender for approval, or reject group home channels for this command; do not grant approval from chatId equality.
  • Bound/expire the dedupe cache and add bridge/adapter regressions for authorization, persistence, and dedupe.
  • Port this onto current bridge.js:637-647, preserving the newer pairing-mode and owner-message-gate behavior.

Automated hermes-sweeper review.

body = (data.get("body") or "").strip()
if home_chat and len(body) >= 6 and body[:6].lower() == "allow ":
hc = self._wa_norm(home_chat)
if hc and (self._wa_norm(data.get("chatId") or "") == hc

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.

WHATSAPP_HOME_CHANNEL is a generic notification/cron chat and may be a group. Matching chatId here grants every allowed member of that group authority to add users. Require a verified owner sender identity, or reject group home channels for approval.

const tsNow = Date.now();
const last = knockSeen.get(senderId) || 0;
if (tsNow - last < KNOCK_DEDUP_MS) return;
knockSeen.set(senderId, tsNow);

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.

knockSeen has no size limit or expiry cleanup, so each distinct rejected sender leaves an entry for the bridge lifetime. Please use a bounded/expiring cache and cover eviction or expiry.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data 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 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low — cosmetic, nice to have platform/whatsapp WhatsApp Business adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform 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 sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants