From db9e3e4ef96bd293ea52af72a94b60556724d3dd Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:42:47 -0700 Subject: [PATCH] docs(discord): troubleshoot silent fail-closed denials Docs portion of PR #57067: 'bot connects but never replies' section pointing at the gateway.log warning and the allowlist/policy knobs. Co-authored-by: ooovenenoso <120500656+ooovenenoso@users.noreply.github.com> --- website/docs/user-guide/messaging/discord.md | 60 +++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/website/docs/user-guide/messaging/discord.md b/website/docs/user-guide/messaging/discord.md index 030e69681e5..5835430c234 100644 --- a/website/docs/user-guide/messaging/discord.md +++ b/website/docs/user-guide/messaging/discord.md @@ -114,7 +114,7 @@ This is the most critical step in the entire setup. Without the correct intents On the **Bot** page, scroll down to **Privileged Gateway Intents**. You'll see three toggles: | Intent | Purpose | Required? | -|--------|---------|-----------| +|--------|---------|-----------| | **Presence Intent** | See user online/offline status | Optional | | **Server Members Intent** | Access the member list, resolve usernames | **Required** | | **Message Content Intent** | Read the text content of messages | **Required** | @@ -170,7 +170,7 @@ This method requires **Public Bot** to be set to **ON** in Step 2. If you set Pu You can construct the invite URL directly using this format: ``` -https://discord.com/oauth2/authorize?client_id=YOUR_APP_ID&scope=bot+applications.commands&permissions=309237763136 +https://discord.com/oauth2/authorize?client_id=YOUR_APP_ID&scope=bot+applications.commands&permissions=274878286912 ``` Replace `YOUR_APP_ID` with the Application ID from Step 1. @@ -187,7 +187,6 @@ These are the minimum permissions your bot needs: ### Recommended Additional Permissions -- **Create Public Threads** - create threads - **Send Messages in Threads** — respond in thread conversations - **Add Reactions** — react to messages for acknowledgment @@ -196,7 +195,7 @@ These are the minimum permissions your bot needs: | Level | Permissions Integer | What's Included | |-------|-------------------|-----------------| | Minimal | `117760` | View Channels, Send Messages, Read Message History, Attach Files | -| Recommended | `309237763136` | All of the above plus Embed Links, Send Messages in Threads, Add Reactions, Create Public Threads | +| Recommended | `274878286912` | All of the above plus Embed Links, Send Messages in Threads, Add Reactions | ## Step 6: Invite to Your Server @@ -272,8 +271,10 @@ Discord behavior is controlled through two files: **`~/.hermes/.env`** for crede | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `DISCORD_BOT_TOKEN` | **Yes** | — | Bot token from the [Discord Developer Portal](https://discord.com/developers/applications). | -| `DISCORD_ALLOWED_USERS` | **Yes** | — | Comma-separated Discord user IDs allowed to interact with the bot. Without this **or** `DISCORD_ALLOWED_ROLES`, the gateway denies all users. | +| `DISCORD_ALLOWED_USERS` | Conditional | — | Comma-separated Discord user IDs allowed to interact with the bot. Without this **or** `DISCORD_ALLOWED_ROLES`, the gateway denies all users unless `DISCORD_ALLOW_ALL_USERS=true`, `GATEWAY_ALLOW_ALL_USERS=true`, or `DISCORD_ALLOWED_CHANNELS` explicitly scopes guild access. | | `DISCORD_ALLOWED_ROLES` | No | — | Comma-separated Discord role IDs. Any member with one of these roles is authorized — OR semantics with `DISCORD_ALLOWED_USERS`. Auto-enables the **Server Members Intent** on connect. Useful when moderation teams churn: new mods get access as soon as the role is granted, no config push needed. | +| `DISCORD_ALLOW_ALL_USERS` | No | `false` | Explicit opt-in to allow every Discord user who can reach the bot. This restores the pre-0.18 open behavior for Discord only; use only for trusted/private guilds or development. | +| `GATEWAY_ALLOW_ALL_USERS` | No | `false` | Global allow-all opt-in for every gateway platform. Prefer the platform-specific `DISCORD_ALLOW_ALL_USERS` unless you intentionally want all connected platforms open. | | `DISCORD_HOME_CHANNEL` | No | — | Channel ID where the bot sends proactive messages (cron output, reminders, notifications). | | `DISCORD_HOME_CHANNEL_NAME` | No | `"Home"` | Display name for the home channel in logs and status output. | | `DISCORD_COMMAND_SYNC_POLICY` | No | `"safe"` | Controls native slash-command startup sync. `"safe"` diffs existing global commands and only updates what changed, recreating commands when Discord metadata changes cannot be applied via patch. `"bulk"` preserves the old `tree.sync()` behavior. `"off"` skips startup sync entirely. | @@ -574,26 +575,6 @@ gateway: Use `/whoami` to see the active scope, your tier (admin / user / unrestricted), and which slash commands you can run. -### Restricting exec-approval buttons to admins - -By default, any user allowed to talk to the bot — including users paired via `hermes pairing approve` — can click the **Approve / Deny** buttons on a dangerous-command prompt. This mirrors plain-chat admission and is the historical behavior. To restrict *approving dangerous commands* to admins only, set `require_admin_for_exec_approval` in the Discord platform's `extra` block: - -```yaml -gateway: - platforms: - discord: - extra: - require_admin_for_exec_approval: true # default: false - allow_admin_from: - - "123456789012345678" # only these users may click Approve/Deny -``` - -**Behavior:** - -- **Default off** — exec-approval buttons stay user-scope; any admitted user can approve. Existing installs are unaffected. -- **When on** — the clicker must pass the normal admission check **and** be listed in `allow_admin_from` (the same key the slash-command split uses). The lower-stakes component views (model picker, clarify, update prompt) stay user-scope. -- **Fails closed** — if the toggle is on but `allow_admin_from` is empty, *nobody* can approve and a warning is logged, so the misconfiguration is visible rather than silently locking you out. - ## Interactive Model Picker Send `/model` with no arguments in a Discord channel to open a dropdown-based model picker: @@ -757,9 +738,34 @@ Refreshing the directory (`/channels refresh` on platforms that expose it, or a ### Bot is online but not responding to messages -**Cause**: Message Content Intent is disabled. +**Cause**: Either Message Content Intent is disabled, or Discord auth is failing closed because no access policy is configured. -**Fix**: Go to [Developer Portal](https://discord.com/developers/applications) → your app → Bot → Privileged Gateway Intents → enable **Message Content Intent** → Save Changes. Restart the gateway. +**Fix**: + +1. Go to [Developer Portal](https://discord.com/developers/applications) → your app → Bot → Privileged Gateway Intents → enable **Message Content Intent** → Save Changes. +2. Verify that at least one Discord access policy is configured: + + ```bash + # recommended: allow specific users + DISCORD_ALLOWED_USERS=284102345871466496 + + # or allow a trusted guild/dev bot to behave like pre-0.18 Discord + DISCORD_ALLOW_ALL_USERS=true + ``` + +3. Restart the gateway: + + ```bash + hermes gateway restart + ``` + +If the gateway log says Discord is connected and REST API checks work, but every inbound message is silent, look for this warning in `~/.hermes/logs/gateway.log`: + +```text +No Discord access policy configured; inbound Discord messages will be denied by default. +``` + +Hermes 0.18 intentionally fails closed on externally reachable adapters. A Discord bot with no `DISCORD_ALLOWED_USERS`, no `DISCORD_ALLOWED_ROLES`, no `DISCORD_ALLOWED_CHANNELS`, and no explicit allow-all flag will connect successfully but deny inbound users before normal message handling. ### "Disallowed Intents" error on startup