Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions assistant/src/runtime/routes/channel-inbound-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import * as externalConversationStore from '../../memory/external-conversation-s
import { getPendingConfirmationsByConversation } from '../../memory/runs-store.js';
import { checkIngressForSecrets } from '../../security/secret-ingress.js';
import { IngressBlockedError } from '../../util/errors.js';
import { getConfig } from '../../config/loader.js';
import { getLogger } from '../../util/logger.js';
import { findMember, updateLastSeen } from '../../memory/ingress-member-store.js';
import {
Expand Down Expand Up @@ -186,12 +185,11 @@ export async function handleChannelInbound(
}

// ── Ingress ACL enforcement ──
const inboxConfig = getConfig().assistantInbox;
// Track the resolved member so the escalate branch can reference it after
// recordInbound (where we have a conversationId).
let resolvedMember: ReturnType<typeof findMember> = null;

if (inboxConfig.enabled && inboxConfig.memberAclEnabled && body.senderExternalUserId) {
if (body.senderExternalUserId) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bypass ACL for guardian control messages

The new unconditional if (body.senderExternalUserId) gate applies ingress member ACL checks before any guardian-specific handling, so senders who are valid guardians but not ingress members are now rejected as not_a_member before /guardian_verify and approval callback paths can run. Guardian identity is tracked in channel_guardian_bindings (not assistant_ingress_members), so this blocks common setup/approval flows where the owner has not been added as a member.

Useful? React with 👍 / 👎.

resolvedMember = findMember({
sourceChannel,
externalUserId: body.senderExternalUserId,
Comment on lines +192 to 195
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.

🔴 Removing feature-flag guard causes unconditional ACL denial for all channel messages when no member records exist

Removing the inboxConfig.enabled && inboxConfig.memberAclEnabled guard makes the ACL member lookup run unconditionally whenever senderExternalUserId is present. The gateway always sends senderExternalUserId for all channels (Telegram, SMS, WhatsApp — see gateway/src/handlers/handle-inbound.ts:83), so this check now fires on every inbound message.

When findMember() finds no matching record in the assistant_ingress_members table (which is the default state for any deployment that hasn't explicitly provisioned ACL member records), it returns null, and the code immediately returns a denial:

if (!resolvedMember) {
  return Response.json({ accepted: true, denied: true, reason: 'not_a_member' });
}
Root Cause and Impact

The feature flag (inboxConfig.enabled && inboxConfig.memberAclEnabled) existed specifically to make ACL enforcement opt-in. Removing the flag without providing a fallback (e.g., skipping ACL when the member table is empty, or auto-provisioning default "allow" records) means that all existing deployments that never configured inbox members will have every inbound channel message silently denied.

The denial response ({ accepted: true, denied: true, reason: 'not_a_member' }) returns HTTP 200, so there's no error signal — messages are silently dropped. This is a breaking behavioral regression for any user who hasn't explicitly set up the ingress member ACL feature.

Actual: Every inbound message with a senderExternalUserId (i.e., all gateway-originated messages) is denied with not_a_member unless the sender has been explicitly registered in assistant_ingress_members.

Expected: Messages should be processed normally for deployments that haven't opted into ACL enforcement. The previous behavior allowed messages through when the inbox feature was disabled.

(Refers to lines 192-216)

Prompt for agents
The ACL enforcement block at assistant/src/runtime/routes/channel-inbound-routes.ts:192-216 needs a fallback so that deployments without any member records in the assistant_ingress_members table are not broken. There are several approaches:

1. (Recommended) Before running ACL enforcement, check if any member records exist at all. If the table is empty, skip ACL enforcement entirely (treat it as "not configured"). This preserves backward compatibility.

2. Alternatively, add a config flag (e.g., ingress.memberAclEnabled) that defaults to false, and gate the ACL block behind it.

3. Or have findMember return a synthetic "allow" member when no records exist for the given assistantId.

The fix should be in assistant/src/runtime/routes/channel-inbound-routes.ts around lines 192-216, and may also require changes to assistant/src/memory/ingress-member-store.ts (e.g., adding a hasMemberRecords() function).
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Expand Down