Discord Interactions
Shiba can be operated from Discord servers and DMs — same approval-gated flow as Slack and Telegram: a /shiba slash command queues a run, a message with Approve / Reject buttons gates it, an approver’s click launches it, and the outcome posts back into the channel. No bot process to host: Discord delivers interactions over HTTP to the Worker you already run.
Before you start
- Approval Gates — how the pending-approval model works
- Telegram Integration — the sibling lane sharing the plumbing
The model
Section titled “The model”Discord channel└── POST /api/discord/interactions (Ed25519 signature auth, 300s window) └── Orchestrator DO named discord:{channel_id} ├── Pending approval ← /shiba task:<task> repo:<url> ├── Approve / Reject ← message buttons, DISCORD_APPROVERS only └── Run → Sandbox → PR → post-back to the same channel- A channel (numeric snowflake id) owns one orchestrator conversation.
- A pending approval is a pointer (
approve:{uuid}); the button click decides it. - A run executes in the Cloudflare Sandbox and posts progress back in-channel.
How It Works
Section titled “How It Works”- Inbound interactions: Discord posts to
POST /api/discord/interactions, signed with Ed25519 — the Worker verifiesX-Signature-Ed25519overtimestamp + bodyusingDISCORD_PUBLIC_KEYand rejects requests older than 300 seconds. - Conversation-per-orchestrator: the Durable Object is named
discord:{channel_id}, so each channel gets its own run registry and approval queue — the same pattern astelegram:{chat_id}. - Slash command:
/shiba task:<task> repo:<github-repo-url>defers inside Discord’s 3-second window (type:5), queues the run, then edits the original reply into an approval card.repo:can be omitted when the channel is mapped inDISCORD_CHANNEL_REPOS. - Approval buttons: the card carries
approve:{uuid}/reject:{uuid}custom_ids — pointers, not capabilities. Only Discord user ids inDISCORD_APPROVERScan decide, and the orchestrator honors a button only while the approval is still pending. - Post-back: run start, completion (with PR link), and failure post back into the channel via
DISCORD_BOT_TOKEN.
-
Create an application in the Discord Developer Portal → New Application. Copy the Public Key from General Information.
-
Store secrets and vars in your Cloudflare environment:
Terminal window pnpm wrangler secret put DISCORD_PUBLIC_KEY --config apps/backend/wrangler.jsoncpnpm wrangler secret put DISCORD_BOT_TOKEN --config apps/backend/wrangler.jsonc# comma-separated Discord user ids allowed to click Approve:pnpm wrangler secret put DISCORD_APPROVERS --config apps/backend/wrangler.jsoncDISCORD_BOT_TOKENcomes from the app’s Bot tab (Reset Token). Your user id needs Developer Mode on → right-click your name → Copy User ID. -
Point Discord at the Worker: General Information → Interactions Endpoint URL →
https://your-worker.workers.dev/api/discord/interactions. Discord sends a signed ping; the Worker answerstype:1only after the signature verifies. -
Register the slash command against your app (and optionally one guild for instant propagation — global commands take up to an hour):
Terminal window curl -X PUT "https://discord.com/api/v10/applications/<APP_ID>/commands" \-H "Authorization: Bot <DISCORD_BOT_TOKEN>" \-H "Content-Type: application/json" \-d '{"name": "shiba","description": "Queue a coding run","options": [{"name": "task", "description": "What to build or fix", "type": 3, "required": true},{"name": "repo", "description": "https://github.com/owner/repo", "type": 3, "required": false}]}' -
Optional:
DISCORD_AGENT_HARNESSpicks the coding agent for Discord runs (falling back toAGENT_HARNESS), andDISCORD_CHANNEL_REPOSmaps a channel id to a default repo sorepo:can be skipped:{"1234567890123456789": "https://github.com/owner/repo"}
Example Interaction
Section titled “Example Interaction”You:
/shiba task: fix the flaky login test repo: https://github.com/owner/repoShiba: Approval requested — Repo: owner/repo · Task: fix the flaky login test
[ Approve ] [ Reject ]
The click only counts if your Discord id is in DISCORD_APPROVERS; everyone else gets an ephemeral refusal. Once approved, the run starts, and Shiba posts the outcome — changed files and the PR link — back into the same channel.
Related documentation
Section titled “Related documentation”- Telegram Integration — the sibling lane, same plumbing
- Slack Integration — the original approval lane
- Configuration & Secrets — every
DISCORD_*variable
