Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Discord

Run your ZeroClaw agent as a Discord bot. This guide walks you through it click by click, no prior bot experience needed. By the end you’ll have a bot sitting in your server that replies when people talk to it.

Who can talk to the agent

Inbound senders are gated against the peer set resolved for the bound agent, drawn from the peer_groups config the agent belongs to. Matching strips a leading @ and is case-insensitive against the channel’s native sender identifier. An empty set denies everyone; a set containing "*" accepts anyone; otherwise only the listed external peers (and peer agents) are accepted. This is separate from gateway pairing (gateway.require_pairing), which authenticates HTTP/WebSocket clients, not chat-channel senders.

A peer group for discord sets channel to discord, lists the allowed senders in external_peers (for discord, the Discord user ID (snowflake); ["*"] accepts anyone), optionally names peer agents for cross-agent dispatch, an ignore blocklist, and an output_modality (mirror, voice, or text). See Peer Groups for the field reference.

Where to set this:

Gateway dashboard

Open /config/peer_groups in the web dashboard.

zerocode

In the Config pane, under Peer groups.

Quickstart

Five steps: make the bot, copy its token, turn on two switches, invite it, and start ZeroClaw.

1. Create the bot

  1. Go to the Discord Developer Portal.
  2. Click New Application, give it a name, and Create.
  3. In the left sidebar, click Bot.
  4. Click Reset Token, then Copy. This long string is your bot_token. Keep it somewhere safe for step 3, you cannot see it again later (only reset it).

The bot token is a password for your bot. Anyone who has it can control your bot. Never paste it into a public chat, screenshot, or commit it to git.

2. Turn on the two switches the bot needs

Still on the Bot page, scroll to Privileged Gateway Intents and toggle both of these on:

  • Message Content Intent so the bot can read what people type.
  • Server Members Intent so it can see who is in the server.

Click Save Changes. If you skip this, the bot connects but never sees any messages, which is the single most common “my bot does nothing” cause.

3. Tell ZeroClaw about the bot

Put the token from step 1 into your config. The token is a secret, so set it through a surface that encrypts it rather than typing it into config.toml:

Gateway dashboard

Open /config/channels/discord in the web dashboard.

zerocode

In the Config pane, under Channels.

channels.discord.<alias>.bot_token is a secret. Stored encrypted, never in plain config.toml. Set it through one of these, which encrypt on write:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.bot_token field there.

zerocode

In the Config pane, set the channels.discord.<alias>.bot_token field (input is masked).

zeroclaw config

zeroclaw config set channels.discord.<alias>.bot_token    # prompts for masked input, stores encrypted

4. Invite the bot to your server

  1. Back in the Developer Portal, open OAuth2 -> URL Generator.
  2. Under Scopes, check bot.
  3. Under Bot Permissions, check at least Send Messages, Read Message History, and View Channels.
  4. Copy the URL at the bottom, open it in your browser, pick your server, and Authorize.

The bot now shows up in your member list (offline until you start ZeroClaw).

5. Start and test

Start ZeroClaw (zeroclaw service restart or zeroclaw daemon), then send a message in a channel the bot can see. It should reply. If it doesn’t, jump to Troubleshooting.

Configuration

The full field list, derived from the live schema. Most have sensible defaults; for a basic bot you only ever set bot_token.

approval_timeout_secs integer · default 300

Seconds to wait for operator approval on always_ask tools before auto-denying.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.approval_timeout_secs field.

zerocode

In the Config pane, set the channels.discord.<alias>.approval_timeout_secs field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.approval_timeout_secs <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__approval_timeout_secs=
archive bool · default false

When true, the channel opens a sidecar discord.db SQLite memory backend, archives every non-bot message it sees, and registers the discord_search tool against it. Default: false. Folded in from the legacy [channels.discord-history] block.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.archive field.

zerocode

In the Config pane, set the channels.discord.<alias>.archive field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.archive <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__archive=
bot_token 🔑 secret · default ""

Discord bot token (from Discord Developer Portal). #[serde(default)] for the same reason as TelegramConfig::bot_token: a missing token must deserialize as an empty string instead of salvage-dropping the alias; validate_bot_token still requires a real token once enabled = true.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.bot_token field.

zerocode

In the Config pane, set the channels.discord.<alias>.bot_token field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.bot_token    # masked input, stored encrypted

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__bot_token=
channel_ids string[] · default []

Channel IDs to watch. Empty = watch every channel the bot can see. Used by the archive sidecar (when archive = true) and by the in-channel filter when set.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.channel_ids field.

zerocode

In the Config pane, set the channels.discord.<alias>.channel_ids field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.channel_ids <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__channel_ids=
draft_update_interval_ms integer · default 1000

Minimum interval (ms) between draft message edits to avoid rate limits. Only used when stream_mode = "partial".

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.draft_update_interval_ms field.

zerocode

In the Config pane, set the channels.discord.<alias>.draft_update_interval_ms field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.draft_update_interval_ms <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__draft_update_interval_ms=
excluded_tools string[] · default []

Tools excluded from this channel’s tool spec. When set, these tools are not exposed to the model when responding via this channel.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.excluded_tools field.

zerocode

In the Config pane, set the channels.discord.<alias>.excluded_tools field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.excluded_tools <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__excluded_tools=
guild_ids string[] · default []

Guild (server) IDs to restrict the bot to. Empty = listen across all guilds the bot is invited to. Migrated from the legacy guild_id singular field.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.guild_ids field.

zerocode

In the Config pane, set the channels.discord.<alias>.guild_ids field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.guild_ids <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__guild_ids=
intents_mask integer? · default null

Raw gateway intent mask override. When set, this exact value is sent in IDENTIFY instead of the derived mask: an operator escape hatch for downstream deployments that consume gateway events through custom builds or forks. The operator owns the consequences: privileged bits still need their Developer Portal toggles, and dropping baseline bits silences message handling (a warning is logged). Unset (default) = derive the mask from the channel’s feature config.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.intents_mask field.

zerocode

In the Config pane, set the channels.discord.<alias>.intents_mask field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.intents_mask <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__intents_mask=
interrupt_on_new_message bool · default false

When true, a newer Discord message from the same sender in the same channel cancels the in-flight request and starts a fresh response with preserved history.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.interrupt_on_new_message field.

zerocode

In the Config pane, set the channels.discord.<alias>.interrupt_on_new_message field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.interrupt_on_new_message <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__interrupt_on_new_message=
listen_to_bots bool · default false

When true, process messages from other bots (not just humans). The bot still ignores its own messages to prevent feedback loops.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.listen_to_bots field.

zerocode

In the Config pane, set the channels.discord.<alias>.listen_to_bots field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.listen_to_bots <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__listen_to_bots=
mention_only bool · default false

When true, only respond to messages that @-mention the bot. Other messages in the guild are silently ignored.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.mention_only field.

zerocode

In the Config pane, set the channels.discord.<alias>.mention_only field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.mention_only <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__mention_only=
multi_message_delay_ms integer · default 800

Delay (ms) between sending each message chunk in multi-message mode. Only used when stream_mode = "multi_message".

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.multi_message_delay_ms field.

zerocode

In the Config pane, set the channels.discord.<alias>.multi_message_delay_ms field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.multi_message_delay_ms <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__multi_message_delay_ms=
proxy_url string? · default null

Per-channel proxy URL (http, https, socks5, socks5h). Overrides the global [proxy] setting for this channel only.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.proxy_url field.

zerocode

In the Config pane, set the channels.discord.<alias>.proxy_url field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.proxy_url <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__proxy_url=
reaction_notifications DiscordReactionScope · default "off"

Which inbound reactions to record: off (default: reaction events are not even requested from the gateway), own (reactions to the bot’s messages), or all (reactions to any message passing the channel’s filters). Recorded reactions are archived to discord.db when archive is enabled, and removed again when a user removes their reaction (bulk clears, remove-all / remove-emoji, are not yet swept). A set intents_mask overrides the derived mask: if it omits the reaction intents, nothing is recorded.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.reaction_notifications field.

zerocode

In the Config pane, set the channels.discord.<alias>.reaction_notifications field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.reaction_notifications <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__reaction_notifications=
reply_min_interval_secs integer · default 0

Per-(channel, recipient) outbound pacing floor in seconds. Range: 0..=REPLY_MIN_INTERVAL_MAX_SECS (0 disables).

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.reply_min_interval_secs field.

zerocode

In the Config pane, set the channels.discord.<alias>.reply_min_interval_secs field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.reply_min_interval_secs <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__reply_min_interval_secs=
reply_queue_depth_max integer · default 0

Per-(channel, recipient) outbound pacing queue depth. Range: 0..=REPLY_QUEUE_DEPTH_CEILING. When reply_min_interval_secs > 0 and this value is 0, the pacing wrapper substitutes DEFAULT_REPLY_QUEUE_DEPTH (16). When the queue is full, the newest send is dropped and a WARN is logged.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.reply_queue_depth_max field.

zerocode

In the Config pane, set the channels.discord.<alias>.reply_queue_depth_max field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.reply_queue_depth_max <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__reply_queue_depth_max=
slash_command_scope SlashCommandScope · default "global"

Scope for registered slash commands: global (default, application-wide, ~1h propagation) or guild (registered to each guild_ids entry, instant). Only meaningful when slash_commands = true; guild with an empty guild_ids warns and falls back to global. Switching scope reaps owned commands from the now-inactive scope; note that removing a guild from guild_ids (without switching scope) does not reap that guild’s commands - remove the bot from the guild, or switch scope, to clear them.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.slash_command_scope field.

zerocode

In the Config pane, set the channels.discord.<alias>.slash_command_scope field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.slash_command_scope <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__slash_command_scope=
slash_commands bool · default false

When true, register and serve Discord slash commands (e.g. /ask) over the Gateway WebSocket, in addition to message handling. Default false. (Prototype: currently registers a single /ask <prompt>.)

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.slash_commands field.

zerocode

In the Config pane, set the channels.discord.<alias>.slash_commands field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.slash_commands <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__slash_commands=
stall_timeout_secs integer · default 0

Stall-watchdog timeout in seconds. When non-zero, the bot will abort and retry if no progress is made within this duration. 0 = disabled.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.stall_timeout_secs field.

zerocode

In the Config pane, set the channels.discord.<alias>.stall_timeout_secs field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.stall_timeout_secs <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__stall_timeout_secs=
stream_mode StreamMode · default "off"

Streaming mode for progressive response delivery. off (default): single message. partial: editable draft updates. multi_message: split response into separate messages at paragraph boundaries.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.stream_mode field.

zerocode

In the Config pane, set the channels.discord.<alias>.stream_mode field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.stream_mode <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__discord__<alias>__stream_mode=

Narrowing where the bot listens

By default the bot listens in every server it’s invited to and every channel it can see. To scope it down:

  • guild_ids: limit the bot to specific servers (guilds). Empty means all.
  • channel_ids: limit it to specific channels. Empty means all visible.

To find an ID, enable Developer Mode in Discord (User Settings -> Advanced), then right-click a server or channel and Copy ID.

Threads and context

When a Discord conversation happens in a thread, that thread is its own conversation. ZeroClaw derives a distinct session key per thread, so every thread carries an independent context window and history: messages in one thread never bleed into another, and the agent does not see a sibling thread’s earlier turns. For Discord, threads are native channels, so each thread is already a separate conversation: no toggle to set.

  • Isolation is the point. Each thread’s context is self-contained: it does not leak outside the thread, and nothing from outside the thread leaks in. Parallel threads hold separate conversational state, so unrelated tasks never contaminate each other.
  • Long threads grow context. A thread accumulates history while it stays active, so a very long thread eventually fills the model’s context window like any other long conversation. Start a new thread to reset.
  • In-flight work is scoped per thread. A new message in one thread does not cancel an in-flight response in another; each thread’s task stands alone.

Set archive = true and the channel opens a sidecar discord.db memory store, records every message it sees, and registers a discord_search tool the agent can use to look up past conversation. Leave it off if you don’t need history search; the bot still replies normally either way.

Streaming

Discord streams replies via the stream_mode setting:

  • off (default): the whole reply posts as one message once the agent finishes. Simplest, and it never shows a half-written answer.
  • partial: the bot posts a draft immediately and edits it in place as the answer streams in. draft_update_interval_ms paces the edits; raise it if Discord rate-limits them.
  • multi_message: each paragraph posts as its own message, separated by multi_message_delay_ms. Good for long answers that would otherwise be one wall of text.

Set it on any surface:

Gateway dashboard

Open /config/channels/discord and set the channels.discord.<alias>.stream_mode field.

zerocode

In the Config pane, set the channels.discord.<alias>.stream_mode field.

zeroclaw config

zeroclaw config set channels.discord.<alias>.stream_mode <value>

Replies that feel natural

  • mention_only: when true, the bot only answers messages that @-mention it, so it stays quiet in busy channels.
  • reply_min_interval_secs: a minimum gap between replies to the same person, useful if instant responses feel robotic.

Troubleshooting

SymptomLikely causeFix
Bot is online but never repliesMessage Content Intent is offTurn it on in the Developer Portal (step 2) and restart
Bot replies nowhereNot invited, or missing View Channels / Send MessagesRe-run the invite (step 4) with the right permissions
Bot ignores most messagesmention_only = true@-mention the bot, or set it to false
“Invalid token” at startupToken mistyped or resetReset the token in the portal, set it again (step 3)

Running Discord as a plugin (experimental)

Everything above uses the Discord channel compiled into ZeroClaw. Discord is also available as an installable WASM channel plugin, the discord package from the plugin registry. The plugin runs inside the plugin sandbox and can reach only the hosts you grant it. It is experimental and handles text messages only; the built-in channel remains the default.

Give each bot token to the built-in channel or the plugin, never both. Discord delivers every event to each connection made with the token, so the two would both answer every message. Before you move a bot to the plugin, remove its [channels.discord.<alias>] table. The plugin never reads that table.

What you need

  • A zeroclaw binary whose plugin host compiles .wasm components, that is, one built with the plugins-wasm-cranelift feature (see build features). On a binary without the plugin host, the zeroclaw plugin command does not exist. zeroclaw plugin install checks that the package loads on this host before it installs anything.
  • The bot from the Quickstart: its token, the Message Content Intent switched on, and the bot invited to your server.
  • In a private channel, the bot (or one of its roles) needs View Channel, Send Messages, and Read Message History on that channel. Discord sends no events for a channel the bot cannot see, and the plugin logs nothing about it: the bot connects and looks healthy but never sees a message. The same applies when channel_ids names a channel the bot cannot view.
  • The numeric user ID of each person allowed to talk to the agent. Enable Developer Mode (User Settings -> Advanced), right-click the user, and click Copy User ID.

1. Install the package and bind an instance

Pick an alias for the instance, here main, and install the package bound to it:

zeroclaw plugin install discord --channel-alias main --egress declared

The command downloads discord from the registry, verifies it, writes a [channels.plugin.main] binding, and creates the instance’s config row. --egress declared grants the hosts the package declares: discord.com for the REST API, and *.discord.gg for the gateway WebSocket, since Discord hands out its gateway and resume hosts under that domain. With --egress none instead, the instance has no network reach until you grant it.

If the package is already installed, bind the alias on its own:

zeroclaw plugin bind discord --channel-alias main --egress declared

Binding a channel instance describes both commands and the report they print.

The instance keeps its settings under a key derived from the package, the channel capability, and the alias. For the alias main the key is zpi1_WyJkaXNjb3JkIiwiY2hhbm5lbCIsIm1haW4iXQ, and zeroclaw plugin info discord prints the key of every bound alias.

2. Set the bot token

The token is a secret. Leave the value off the command line and zeroclaw config set prompts for it without echo and stores it encrypted:

zeroclaw config set plugins.entries.zpi1_WyJkaXNjb3JkIiwiY2hhbm5lbCIsIm1haW4iXQ.config.bot_token

The other settings sit beside the token under .config. and are optional: guild_ids and channel_ids narrow where the bot listens (see Narrowing where the bot listens), and mention_only and listen_to_bots take true or false. ZeroClaw stores every plugin setting encrypted, so zeroclaw config set prompts for each one as it does for the token; pass --no-interactive to give a setting that is not secret on the command line. Set a list as JSON text:

zeroclaw config set --no-interactive plugins.entries.zpi1_WyJkaXNjb3JkIiwiY2hhbm5lbCIsIm1haW4iXQ.config.channel_ids '["123456789012345678"]'

3. Turn it on and route it to an agent

Binding never turns on the plugin system and never assigns the instance to an agent. Turn the plugin system on:

zeroclaw config set plugins.enabled true

Then add plugin.main to the channels list of the agent that should answer, keeping the channels it already has, and admit your users with a peer group on the same channel:

[agents.assistant]
channels = ["plugin.main"]

[peer_groups.discord_plugin]
channel = "plugin.main"
external_peers = ["111111111111111111"]

The plugin reports each sender by numeric user ID, and a plugin channel matches senders exactly, so list user IDs, not usernames. Restart ZeroClaw (zeroclaw service restart) to start the instance.

4. Check that it runs

  • zeroclaw plugin info discord reports whether the package loads on this host, prints the instance key, and lists anything the instance still needs before it can start.
  • At startup, the daemon’s channel list includes plugin.main, and the gateway’s /health snapshot lists a channel:plugin.main component. The component reads error when the plugin’s poll fails, and otherwise follows the plugin’s own health check, which ZeroClaw asks for about every 30 seconds. What that check covers is up to the plugin, so the proof that the bot is connected to Discord is a message from an admitted user reaching the agent and the agent’s reply appearing in Discord.
  • A message from an admitted user is logged as channel inbound message with zeroclaw.channel = plugin.main. A message from anyone else is dropped and logged with error_key = plugin_channel_sender_denied.
  • A connection to a host outside the grant is refused. A refused HTTP request is logged with error_key = plugin_egress_denied, the refused host, and the zeroclaw config set command that grants it. A refused gateway connection is not logged yet, so a bot that stays offline after a change to egress_hosts has most likely lost *.discord.gg.

How the plugin differs from the built-in channel

Built-in channeldiscord plugin
Configuration[channels.discord.<alias>][channels.plugin.<alias>] and the instance’s plugins.entries row
Channel name in logs, peer groups, and sessionsdiscord.<alias>plugin.<alias>
Network reachNo per-host grantOnly the hosts in the instance’s egress_hosts
FeaturesEverything on this pageText messages only: no attachments, threads, slash commands, reactions, streaming, typing indicators, or archive search

Conversations are kept per channel name, so a bot moved from discord.main to plugin.main starts new conversations.

See also