Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nearai/ironclaw/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Channels receive messages from external sources (CLI, HTTP, WebSockets, etc.) and convert them to a unified message format for the agent to process. The channel system supports:
  • Multiple concurrent input channels
  • Unified message abstraction
  • Streaming message delivery
  • Channel-specific metadata
  • WASM-based dynamic channel loading

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                         ChannelManager                              │
│                                                                     │
│   ┌──────────────┐   ┌─────────────┐   ┌─────────────┐             │
│   │ ReplChannel  │   │ HttpChannel │   │ WasmChannel │   ...       │
│   └──────┬───────┘   └──────┬──────┘   └──────┬──────┘             │
│          │                 │                 │                      │
│          └─────────────────┴─────────────────┘                      │
│                            │                                        │
│                   select_all (futures)                              │
│                            │                                        │
│                            ▼                                        │
│                     MessageStream                                   │
└─────────────────────────────────────────────────────────────────────┘

Core Types

Channel Trait

The main trait that all channels must implement.
#[async_trait]
pub trait Channel: Send + Sync {
    fn name(&self) -> &str;
    fn message_stream(&self) -> MessageStream;
    async fn send(&self, response: OutgoingResponse) -> Result<(), ChannelError>;
    async fn send_status(&self, status: StatusUpdate) -> Result<(), ChannelError>;
}

Methods

name
fn(&self) -> &str
Return the channel name (must be unique)
message_stream
fn(&self) -> MessageStream
Get a stream of incoming messages from this channel
send
async fn(&self, response: OutgoingResponse) -> Result<()>
Send a response back through this channel
send_status
async fn(&self, status: StatusUpdate) -> Result<()>
Send a status update (typing indicator, progress, etc.)

IncomingMessage

A message received from an external channel.
id
Uuid
Unique message identifier
channel
String
Channel this message came from
user_id
String
User identifier within the channel
user_name
Option<String>
Optional display name for the user
content
String
Message content/text
thread_id
Option<String>
Thread/conversation ID for threaded conversations
received_at
DateTime<Utc>
When the message was received
metadata
serde_json::Value
Channel-specific metadata

Constructors

new
fn(channel: impl Into<String>, user_id: impl Into<String>, content: impl Into<String>) -> Self
Create a new incoming message
with_thread
fn(self, thread_id: impl Into<String>) -> Self
Set the thread ID
with_metadata
fn(self, metadata: serde_json::Value) -> Self
Set channel-specific metadata
with_user_name
fn(self, name: impl Into<String>) -> Self
Set the user’s display name

Example

use ironclaw::channels::IncomingMessage;

let msg = IncomingMessage::new("slack", "U12345", "Hello agent!")
    .with_thread("C67890")
    .with_user_name("Alice");

OutgoingResponse

A response to send back through a channel.
content
String
The content to send
thread_id
Option<String>
Optional thread ID to reply in
attachments
Vec<String>
Optional file paths to attach
metadata
serde_json::Value
Channel-specific metadata for the response

Constructors

text
fn(content: impl Into<String>) -> Self
Create a simple text response
in_thread
fn(self, thread_id: impl Into<String>) -> Self
Set the thread ID for the response
with_attachments
fn(self, attachments: Vec<String>) -> Self
Add file attachments
with_metadata
fn(self, metadata: serde_json::Value) -> Self
Set channel-specific metadata

Example

use ironclaw::channels::OutgoingResponse;

let response = OutgoingResponse::text("Task completed!")
    .in_thread("C67890")
    .with_attachments(vec!["report.pdf".to_string()]);

channel.send(response).await?;

StatusUpdate

Status updates sent during processing (typing indicators, progress, etc.).
kind
StatusKind
Type of status update
message
Option<String>
Optional status message
progress
Option<f32>
Optional progress (0.0 to 1.0)

StatusKind

Typing
()
Agent is typing/thinking
Processing
()
Agent is processing the request
ToolExecution
String
Agent is executing a tool (includes tool name)
Complete
()
Processing is complete

Example

use ironclaw::channels::{StatusUpdate, StatusKind};

let status = StatusUpdate {
    kind: StatusKind::ToolExecution("http".to_string()),
    message: Some("Fetching data...".to_string()),
    progress: Some(0.5),
};

channel.send_status(status).await?;

MessageStream

A stream of incoming messages from a channel.
pub type MessageStream = Pin<Box<dyn Stream<Item = IncomingMessage> + Send>>;
Use with async stream combinators:
use futures::StreamExt;

let mut stream = channel.message_stream();
while let Some(msg) = stream.next().await {
    println!("Received: {} from {}", msg.content, msg.user_id);
}

Built-in Channels

ReplChannel

Interactive command-line REPL channel.
new
fn() -> Self
Create a new REPL channel with stdin/stdout
with_prompt
fn(prompt: impl Into<String>) -> Self
Set a custom prompt string
use ironclaw::channels::ReplChannel;

let repl = ReplChannel::new().with_prompt("ironclaw> ");

HttpChannel

HTTP webhook receiver channel.
new
fn(addr: impl Into<SocketAddr>) -> Self
Create a new HTTP channel listening on the given address
with_auth
fn(self, token: impl Into<String>) -> Self
Add bearer token authentication
use ironclaw::channels::HttpChannel;

let http = HttpChannel::new("127.0.0.1:8080")
    .with_auth("secret-token-123");

HTTP Endpoint

The HTTP channel exposes a POST endpoint:
curl -X POST http://localhost:8080/message \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer secret-token-123" \
  -d '{
    "user_id": "user_123",
    "content": "Hello agent!",
    "thread_id": "thread_456"
  }'

SignalChannel

Unix signal handler channel (SIGUSR1, SIGUSR2, etc.).
new
fn(signals: Vec<Signal>) -> Self
Create a signal channel that listens for the specified Unix signals
use ironclaw::channels::SignalChannel;
use signal_hook::consts::signal::*;

let signal_ch = SignalChannel::new(vec![SIGUSR1, SIGUSR2]);

GatewayChannel

WebSocket gateway for real-time bidirectional communication.
new
fn(addr: impl Into<SocketAddr>) -> Self
Create a WebSocket gateway listening on the given address
with_auth
fn(self, auth_fn: AuthFn) -> Self
Add custom authentication function
use ironclaw::channels::GatewayChannel;

let gateway = GatewayChannel::new("127.0.0.1:9000");

Channel Manager

ChannelManager

Manages multiple channels and multiplexes their message streams.
new
fn() -> Self
Create a new channel manager
register
fn(&mut self, channel: Arc<dyn Channel>)
Register a channel with the manager
message_stream
fn(&self) -> MessageStream
Get a unified stream of messages from all registered channels
send
async fn(&self, channel_name: &str, response: OutgoingResponse) -> Result<()>
Send a response to a specific channel

Example

use ironclaw::channels::{ChannelManager, ReplChannel, HttpChannel};
use futures::StreamExt;

let mut manager = ChannelManager::new();
manager.register(Arc::new(ReplChannel::new()));
manager.register(Arc::new(HttpChannel::new("127.0.0.1:8080")));

let mut stream = manager.message_stream();
while let Some(msg) = stream.next().await {
    // Process messages from all channels
    println!("[{}] {}: {}", msg.channel, msg.user_id, msg.content);
    
    // Send response back through the originating channel
    manager.send(
        &msg.channel,
        OutgoingResponse::text("Received!")
    ).await?;
}

WASM Channels

Dynamically load channel implementations at runtime using WebAssembly.

WasmChannel

A channel implementation loaded from a WASM module.
load
async fn(wasm_bytes: &[u8]) -> Result<Self>
Load a WASM module as a channel
from_file
async fn(path: &Path) -> Result<Self>
Load a WASM channel from a file
use ironclaw::channels::wasm::WasmChannel;

let wasm_bytes = std::fs::read("channels/discord.wasm")?;
let channel = WasmChannel::load(&wasm_bytes).await?;

manager.register(Arc::new(channel));

WASM Channel Interface

WASM channels must implement the following interface:
// Export these functions from your WASM module
#[no_mangle]
pub extern "C" fn channel_name() -> *const u8;

#[no_mangle]
pub extern "C" fn channel_init() -> i32;

#[no_mangle]
pub extern "C" fn channel_poll_message() -> *const u8;

#[no_mangle]
pub extern "C" fn channel_send_response(response_ptr: *const u8, response_len: usize) -> i32;
See the wasm module documentation for full details on the WASM channel ABI.

Error Handling

ChannelError

Errors that can occur during channel operations.
ConnectionFailed
String
Failed to establish connection
SendFailed
String
Failed to send message/response
InvalidMessage
String
Received malformed message
AuthenticationFailed
String
Authentication/authorization failed
NotFound
String
Channel or resource not found

WebSocket Gateway

The GatewayChannel provides a production-ready WebSocket server for real-time communication.

Client Connection

const ws = new WebSocket('ws://localhost:9000');

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: 'message',
    user_id: 'user_123',
    content: 'Hello agent!',
    thread_id: 'thread_456'
  }));
};

ws.onmessage = (event) => {
  const response = JSON.parse(event.data);
  console.log('Agent:', response.content);
};

Status Updates

The gateway sends real-time status updates:
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  
  if (msg.type === 'status') {
    console.log('Status:', msg.kind, msg.message);
  } else if (msg.type === 'response') {
    console.log('Response:', msg.content);
  }
};

Best Practices

  1. Channel isolation: Each channel should handle its own protocol/transport details
  2. Unified messages: Convert channel-specific formats to IncomingMessage early
  3. Metadata: Use the metadata field for channel-specific context (message IDs, etc.)
  4. Error handling: Channels should gracefully handle disconnections and reconnect
  5. Authentication: Validate users before accepting messages
  6. Rate limiting: Consider rate limiting per user/channel to prevent abuse

Agent Module

Agent orchestration and message processing

Tools Module

Tools that agents can use to respond to messages

Build docs developers (and LLMs) love