Add Discord
Discord is a shipped Virgo chat adapter. It owns Discord Gateway v10, Bot API v10, native identities, exact return addresses, reaction state and attachment transfer. It does not create a second routing policy, a Seat per bot, or one bot per Seat.
Read Connect a chat app to Virgo first if you have not. In particular: registering an adapter on an installation you already run is supported through adapter add, so the steps below end at that command rather than at a descriptor you can only prepare.
Before you start
- A Virgo installation you already run, with its Hub reachable.
- A Discord server you administer, existing or new.
- An application and bot in the Discord Developer Portal. Create these through Discord's own documentation; Virgo does not create applications or bots for you.
Minimum permissions
Install the bot to the guilds you intend to use, and grant only the channel permissions the features you want actually need:
- View Channels
- Send Messages
- Read Message History
- Add Reactions
- Attach Files
Do not grant Administrator.
Enable the Message Content intent. Virgo's Gateway client requests Guilds, Guild Messages, Direct Messages, message reactions and Message Content. Discord may require privileged-intent approval once an application reaches its review scale.
Store the bot token privately
Put the bot token in Virgo's credential store and reference it by id. The descriptor holds credential:..., never the token itself.
Never put a bot token in virgo.config.json, on a command line, in a Git file, or in a Hub message.
The descriptor you will need
Placeholders only. Replace every value with your own; the IDs below are shaped like Discord snowflakes but are not real.
{
"id": "discord-main",
"kind": "discord",
"session": "discord-session",
"vsp": "vsp:/your-account:your-space/discord/bot",
"credential": "credential:discord/main",
"applicationId": "000000000000000001",
"botUserId": "000000000000000002",
"targetVsp": "vsp:/your-account:your-space/your-repo/lead",
"gatewayPort": "adapter-port:discord/gateway",
"reactionStatePort": "adapter-port:discord/reactions",
"artifactPort": "adapter-port:artifacts/managed",
"allowedGuildIds": ["000000000000000003"],
"allowedChannelIds": ["000000000000000004"],
"defaultRoute": {
"guildId": "000000000000000003",
"channelId": "000000000000000004"
},
"accessNotificationRoute": {
"guildId": "000000000000000003",
"channelId": "000000000000000004"
},
"maximumInboundAttachmentBytes": 10000000,
"maximumOutboundAttachmentBytes": 10000000,
"access": {
"administrators": ["discord.user:000000000000000007"]
}
}
targetVsp is where accepted messages go. allowedChannelIds may name a parent channel, and messages in its Discord threads stay in scope. defaultRoute is the default outbound conversation; accessNotificationRoute is where approval requests are announced.
Set both byte ceilings explicitly. Discord's own upload limits vary by account, guild, tier and provider experiment, so the configured number is Virgo's acceptance ceiling, not a claim about a universal Discord limit.
Senders and administrators
The principal is discord.user:<user-id>. Mutable usernames and display names are diagnostic only. Bot authors, webhooks, Discord system message types and the configured bot itself never enter the routing parser.
Discord defaults to approval access. If you do not set access.mode, as the descriptor above does not, a new sender's first message is held until an administrator decides. Installation refuses a descriptor that defaults to approval without an accessNotificationRoute to announce the request on, so that field is effectively required — the example sets it.
Administrators listed in access.administrators decide pending requests with /virgo-access approve <requestId> or /virgo-access deny <requestId>. Without at least one administrator, nothing can approve a first message.
Registering it on an installation you already have
install accepts the descriptor above through --adapters with --host-settings when you are building a new installation. When the installation already exists, the supported route is adapter add:
adapter add --file /absolute/path/to/discord.json
adapter list
adapter remove discord-main
The path must be absolute. An id already in the roster is refused as a conflict rather than replaced, only the new entry is prepared, and the whole roster is then validated the way a boot validates it — so an add cannot leave a configuration that will not start. A credentialBinding in the descriptor is registered in the same operation rather than as a separate step. adapter list reads the roster back with the credential reference each entry names, never the secret itself.
It becomes active on the next Host start. A running Host keeps the adapters it booted with, and native agent sessions are left running.
Do not reinstall your Hub to get a Discord bot.
What has been accepted, and what has not
One canonical route has loaded this adapter into an installation that already existed and reached Gateway READY. That is where acceptance currently stops. An agent claiming its Seat over the adapter, a human message arriving inbound, and a correlated reply going back out have not been accepted, so do not plan around them yet.
Source tests and configuration validation are not field acceptance either. On an isolated installation the chain is checked in order:
- the release contains
adapters/discord/adapter.jsonand the exact module bytes; - the Host boots the configured instance and
/users/@meproves the expected bot id; - Gateway READY/RESUMED, heartbeat ACK and persisted sequence recovery work;
- a human message reaches Hub authorisation and the selected or default Seat;
- an explicit additional recipient is additive and receives the same accepted request;
- each agent reply returns to the original guild, channel, thread and message with a concise
[Virgo · Seat · VSP]label; - attachment upload and download, reaction add, replace and remove, restart replay, and refusal of bot, webhook, system and self messages are observed in Discord.
Files and reactions
Incoming attachments are downloaded from Discord's CDN before the Gateway cursor is acknowledged, then written to the managed artifact store with participant ACLs and retention metadata. Expired downloads, changed sizes, over-limit payloads and untrusted URLs are refused.
Reactions use the provider's add and remove-own-reaction routes. The adapter persists its exact last reaction per message, so removing a reaction removes only its own known emoji after a restart; it never calls a delete-all route.
Troubleshooting and removal
- No inbound messages. Check the Message Content intent, that the bot is in the guild, and that the channel appears in
allowedChannelIds. - Messages arrive but nothing replies. That is an access or routing question, not a provider one — check the access mode and whether a request is pending an administrator decision.
- Attachments refused. Compare the payload against the configured inbound ceiling; the configured ceiling is the binding one.
- Removal. Revoke the bot's access in Discord and retire the credential.
adapter remove <id>takes it out of an existing installation's configuration, and the Host stops serving it at the next start.
What is verified today
The adapter source is integrated and its configuration validates. No bot credential, external post, live installation or Discord field acceptance is claimed by that. See Availability and evidence for the dated position.
Protocol references: Gateway, Gateway events, messages, authentication.