Files
optimclaw/channels-src/discord
a7c0be7f1b fix: Discord Ed25519 signature verification and capabilities header alias (#148) (#372)
* test: add failing tests for Discord signature validation and capabilities alias (Red phase)

TDD Red phase for #148. Adds 19 tests across 4 categories:
- Category 1: CredentialLocationSchema header_name alias (2 failing)
- Category 2: Ed25519 signature verification (3 failing)
- Category 3: Router signature key management (2 failing)
- Category 5: Discord capabilities public_key setup (1 failing)

All 8 failures are expected — stubs return false/None by design.
Implementation will follow in Green phase.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: add Discord Ed25519 signature verification and capabilities alias (#148)

Implement the Green phase for Discord channel security fixes:

- Add real Ed25519 signature verification in signature.rs using ed25519-dalek
- Add #[serde(alias = "header_name")] to CredentialLocationSchema::Header
  for backward compatibility with external JSON files
- Add signature_keys storage to WasmChannelRouter (register/get/unregister)
- Add discord_public_key to discord.capabilities.json setup.required_secrets
- Add nested capabilities resolution to CapabilitiesFile for channel-level
  JSON compatibility

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* style: address PR #372 review comments

- Fix invalid hex character in test fake_pub_key (router.rs)
- Simplify signature parsing with from_slice/try_from (signature.rs)
- Use idiomatic Option::or for nested capability merging (capabilities_schema.rs)

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: enforce signature verification, staleness check, key validation, recursive resolve

Address PR #372 review feedback:

- Wire verify_discord_signature() into webhook_handler with Ed25519
  signature + timestamp staleness check (5s window via now_secs param)
- Validate Ed25519 keys in register_signature_key() (hex decode +
  VerifyingKey::try_from) before storing, return Result<(), String>
- Recursively resolve nested capabilities in resolve_nested()
- Add 25 new tests: 8 staleness, 6 key validation, 7 webhook
  integration (tower::oneshot), 4 resolve_nested edge cases
- Fix pre-existing clippy warning in signal.rs

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: wire register_signature_key() into all channel loading paths

The Ed25519 signature key registration was implemented and tested but
never called from production code. All three channel loading paths
(setup_wasm_channels, activate_wasm_channel, refresh_active_channel)
now read the public key from the secrets store and register it with
the webhook router, enabling Discord signature verification.

Adds `signature_key_secret_name` field to WebhookSchema so channels
can declare which secret contains their Ed25519 public key.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-02-27 07:01:54 +00:00
..

Discord Channel for IronClaw

WASM channel for Discord integration - handle slash commands and button interactions via webhooks.

Features

  • Slash Commands - Process Discord slash commands
  • Button Interactions - Handle button clicks
  • Thread Support - Respond in threads
  • DM Support - Handle direct messages

Setup

  1. Create a Discord Application at https://discord.com/developers/applications

  2. Create a Bot and get the token

  3. Set up Interactions URL to point to your IronClaw instance

  4. Copy the Application ID and Public Key

  5. Store in IronClaw secrets:

    ironclaw secret set discord_bot_token YOUR_BOT_TOKEN
    

    Note: The discord_bot_token secret is the only value read directly by this Discord channel WASM component. The discord_app_id and discord_public_key secrets are used by the IronClaw host (for example, to verify Discord interaction signatures and manage slash command registration) and are not accessed from the WASM module itself.

Discord Configuration

Register Slash Commands

curl -X POST \
  -H "Authorization: Bot YOUR_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  https://discord.com/api/v10/applications/YOUR_APP_ID/commands \
  -d '{
    "name": "ask",
    "description": "Ask the AI agent",
    "options": [{
      "name": "question",
      "description": "Your question",
      "type": 3,
      "required": true
    }]
  }'

Set Interactions Endpoint

In your Discord app settings, set:

  • Interactions Endpoint URL: https://your-ironclaw.com/webhook/discord

Usage Examples

Slash Command

User types: /ask question: What is the weather?

The agent receives:

User: @username
Content: /ask question: What is the weather?

Button Click

When a user clicks a button in a message, the agent receives:

User: @username
Content: [Button clicked] Original message content

Error Handling

If an internal error occurs (e.g., metadata serialization failure), the tool attempts to send an ephemeral message to the user:

❌ Internal Error: Failed to process command metadata.

Check the host logs for detailed error information.

Advanced Usage

Embeds

To send embeds, include an embeds array in the metadata_json field of the agent's response. The structure should match the Discord API embed object.

Troubleshooting

"Invalid Signature"

  • Check that discord_public_key is set correctly in IronClaw secrets.
  • This validation happens on the host before reaching the WASM.

"401 Unauthorized"

  • Check that discord_bot_token is set correctly in IronClaw secrets.
  • Ensure the bot is added to the server.

"Interaction Failed"

  • The interaction might have timed out (Discord requires a response within 3 seconds).
  • The interactions_endpoint_url might be unreachable.

Building

cd channels-src/discord
cargo build --target wasm32-wasi --release

License

MIT/Apache-2.0