Skip to main content
Channels are WASM components that handle communication with external messaging platforms (Telegram, WhatsApp, Slack, etc.). They run in a sandboxed environment and communicate with the host via the WIT (WebAssembly Interface Types) interface.

Prerequisites

Install Rust and add the WASM target:
Optional but useful for component conversion workflows:

1. Create the project structure

Create a new crate with this layout:

2. Configure Cargo.toml

Cargo.toml

3. Implement the channel interface

Implement the channel guest interface from wit/channel.wit:

Required Imports

Implementing the Guest Trait

on_start configures how the channel registers with its platform API. The manifest’s [channel.ingress] controls how the host routes inbound webhooks — they are separate concerns. on_http_request and on_poll ingest external messages; on_respond delivers replies to an existing conversation; on_broadcast sends proactive messages; on_status lets channels surface thinking indicators and other progress updates.

4. Preserve routing metadata (critical)

Once your channel can receive messages, keep enough metadata to send responses back to the right chat and sender. Store routing info in message metadata so responses can be delivered to the right chat and sender.
response.metadata_json contains the metadata from the original inbound message. Treat it as the source of truth for reply routing.

5. Add secure credential placeholders

Now that the message path is set, configure API credentials using placeholders instead of hardcoded tokens.
Never hardcode credentials. Use placeholders that the host replaces.

URL Placeholders (Telegram-style)

Header Placeholders (WhatsApp-style)

The placeholder format is {SECRET_NAME} where SECRET_NAME matches the credential name in uppercase with underscores (e.g., whatsapp_access_token{WHATSAPP_ACCESS_TOKEN}).

6. Create the extension manifest

Channels are installed as extensions. The manifest declares the runtime surface and channel configuration in TOML format:
my-channel/manifest.toml
Message-size limits are provider protocol details. Enforce the provider’s actual unit (bytes, Unicode scalar values, or UTF-16 code units) inside the channel adapter while rendering and chunking outbound parts. Place the manifest alongside the WASM binary with this layout:

7. Build and install

Build the WASM component and place it in the extension asset directory alongside the manifest.
The extension is installed through the IronClaw extension lifecycle. For host-bundled channels, add the package as a self-contained directory crates/extensions/packages/<channel>/ (manifest + schemas + prompts + WASM) with a package module crates/extensions/ironclaw_extension_support/src/packages/<channel>.rs and its row in PACKAGES in .../src/packages/mod.rs — there is no separate registration file. At runtime every package is discovered at the virtual package root /system/extensions/<extension-id>/manifest.toml. Users install discovered packages by ID: ironclaw extension search lists them, and ironclaw extension install <extension-id> installs one — it takes an extension ID from search results, not a filesystem path. The WebUI Extensions area exposes the same list/install/import/remove lifecycle. For local development, place the extension directory under <reborn-home>/local-dev/system/extensions/<channel>/.

8. Host functions you can call

Core APIs (every channel uses these)

Advanced APIs (pairing, attachments)

Channels that download binary payloads (voice notes, images) or support owner approval for unknown senders can use these:

9. Common patterns

Polling with stored offsets

Ignore status-only payloads

Ignore bot senders


10. Testing and troubleshooting

Add tests for message parsing and metadata round-trips. The key assertions are that routing metadata is preserved through serialization:
If you see byte index N is not a char boundary, avoid byte slicing and truncate by characters:
If credential placeholders are not resolved:
  1. Verify secret names match the declared placeholders in the manifest.
  2. Confirm the credential handle matches what the channel code expects.
  3. Check runtime logs for unresolved placeholder warnings.