> ## 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.

# Channels Module

> Multi-channel input system for receiving messages from external sources

## 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

```text theme={null}
┌─────────────────────────────────────────────────────────────────────┐
│                         ChannelManager                              │
│                                                                     │
│   ┌──────────────┐   ┌─────────────┐   ┌─────────────┐             │
│   │ ReplChannel  │   │ HttpChannel │   │ WasmChannel │   ...       │
│   └──────┬───────┘   └──────┬──────┘   └──────┬──────┘             │
│          │                 │                 │                      │
│          └─────────────────┴─────────────────┘                      │
│                            │                                        │
│                   select_all (futures)                              │
│                            │                                        │
│                            ▼                                        │
│                     MessageStream                                   │
└─────────────────────────────────────────────────────────────────────┘
```

## Core Types

### Channel Trait

The main trait that all channels must implement.

```rust theme={null}
#[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

<ResponseField name="name" type="fn(&self) -> &str">
  Return the channel name (must be unique)
</ResponseField>

<ResponseField name="message_stream" type="fn(&self) -> MessageStream">
  Get a stream of incoming messages from this channel
</ResponseField>

<ResponseField name="send" type="async fn(&self, response: OutgoingResponse) -> Result<()>">
  Send a response back through this channel
</ResponseField>

<ResponseField name="send_status" type="async fn(&self, status: StatusUpdate) -> Result<()>">
  Send a status update (typing indicator, progress, etc.)
</ResponseField>

### IncomingMessage

A message received from an external channel.

<ParamField path="id" type="Uuid">
  Unique message identifier
</ParamField>

<ParamField path="channel" type="String">
  Channel this message came from
</ParamField>

<ParamField path="user_id" type="String">
  User identifier within the channel
</ParamField>

<ParamField path="user_name" type="Option<String>">
  Optional display name for the user
</ParamField>

<ParamField path="content" type="String">
  Message content/text
</ParamField>

<ParamField path="thread_id" type="Option<String>">
  Thread/conversation ID for threaded conversations
</ParamField>

<ParamField path="received_at" type="DateTime<Utc>">
  When the message was received
</ParamField>

<ParamField path="metadata" type="serde_json::Value">
  Channel-specific metadata
</ParamField>

#### Constructors

<ResponseField name="new" type="fn(channel: impl Into<String>, user_id: impl Into<String>, content: impl Into<String>) -> Self">
  Create a new incoming message
</ResponseField>

<ResponseField name="with_thread" type="fn(self, thread_id: impl Into<String>) -> Self">
  Set the thread ID
</ResponseField>

<ResponseField name="with_metadata" type="fn(self, metadata: serde_json::Value) -> Self">
  Set channel-specific metadata
</ResponseField>

<ResponseField name="with_user_name" type="fn(self, name: impl Into<String>) -> Self">
  Set the user's display name
</ResponseField>

#### Example

```rust theme={null}
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.

<ParamField path="content" type="String">
  The content to send
</ParamField>

<ParamField path="thread_id" type="Option<String>">
  Optional thread ID to reply in
</ParamField>

<ParamField path="attachments" type="Vec<String>">
  Optional file paths to attach
</ParamField>

<ParamField path="metadata" type="serde_json::Value">
  Channel-specific metadata for the response
</ParamField>

#### Constructors

<ResponseField name="text" type="fn(content: impl Into<String>) -> Self">
  Create a simple text response
</ResponseField>

<ResponseField name="in_thread" type="fn(self, thread_id: impl Into<String>) -> Self">
  Set the thread ID for the response
</ResponseField>

<ResponseField name="with_attachments" type="fn(self, attachments: Vec<String>) -> Self">
  Add file attachments
</ResponseField>

<ResponseField name="with_metadata" type="fn(self, metadata: serde_json::Value) -> Self">
  Set channel-specific metadata
</ResponseField>

#### Example

```rust theme={null}
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.).

<ParamField path="kind" type="StatusKind">
  Type of status update
</ParamField>

<ParamField path="message" type="Option<String>">
  Optional status message
</ParamField>

<ParamField path="progress" type="Option<f32>">
  Optional progress (0.0 to 1.0)
</ParamField>

#### StatusKind

<ResponseField name="Typing" type="()">
  Agent is typing/thinking
</ResponseField>

<ResponseField name="Processing" type="()">
  Agent is processing the request
</ResponseField>

<ResponseField name="ToolExecution" type="String">
  Agent is executing a tool (includes tool name)
</ResponseField>

<ResponseField name="Complete" type="()">
  Processing is complete
</ResponseField>

#### Example

```rust theme={null}
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.

```rust theme={null}
pub type MessageStream = Pin<Box<dyn Stream<Item = IncomingMessage> + Send>>;
```

Use with async stream combinators:

```rust theme={null}
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.

<ResponseField name="new" type="fn() -> Self">
  Create a new REPL channel with stdin/stdout
</ResponseField>

<ResponseField name="with_prompt" type="fn(prompt: impl Into<String>) -> Self">
  Set a custom prompt string
</ResponseField>

```rust theme={null}
use ironclaw::channels::ReplChannel;

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

### HttpChannel

HTTP webhook receiver channel.

<ResponseField name="new" type="fn(addr: impl Into<SocketAddr>) -> Self">
  Create a new HTTP channel listening on the given address
</ResponseField>

<ResponseField name="with_auth" type="fn(self, token: impl Into<String>) -> Self">
  Add bearer token authentication
</ResponseField>

```rust theme={null}
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:

```bash theme={null}
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.).

<ResponseField name="new" type="fn(signals: Vec<Signal>) -> Self">
  Create a signal channel that listens for the specified Unix signals
</ResponseField>

```rust theme={null}
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.

<ResponseField name="new" type="fn(addr: impl Into<SocketAddr>) -> Self">
  Create a WebSocket gateway listening on the given address
</ResponseField>

<ResponseField name="with_auth" type="fn(self, auth_fn: AuthFn) -> Self">
  Add custom authentication function
</ResponseField>

```rust theme={null}
use ironclaw::channels::GatewayChannel;

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

## Channel Manager

### ChannelManager

Manages multiple channels and multiplexes their message streams.

<ResponseField name="new" type="fn() -> Self">
  Create a new channel manager
</ResponseField>

<ResponseField name="register" type="fn(&mut self, channel: Arc<dyn Channel>)">
  Register a channel with the manager
</ResponseField>

<ResponseField name="message_stream" type="fn(&self) -> MessageStream">
  Get a unified stream of messages from all registered channels
</ResponseField>

<ResponseField name="send" type="async fn(&self, channel_name: &str, response: OutgoingResponse) -> Result<()>">
  Send a response to a specific channel
</ResponseField>

#### Example

```rust theme={null}
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.

<ResponseField name="load" type="async fn(wasm_bytes: &[u8]) -> Result<Self>">
  Load a WASM module as a channel
</ResponseField>

<ResponseField name="from_file" type="async fn(path: &Path) -> Result<Self>">
  Load a WASM channel from a file
</ResponseField>

```rust theme={null}
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:

```rust theme={null}
// 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.

<ResponseField name="ConnectionFailed" type="String">
  Failed to establish connection
</ResponseField>

<ResponseField name="SendFailed" type="String">
  Failed to send message/response
</ResponseField>

<ResponseField name="InvalidMessage" type="String">
  Received malformed message
</ResponseField>

<ResponseField name="AuthenticationFailed" type="String">
  Authentication/authorization failed
</ResponseField>

<ResponseField name="NotFound" type="String">
  Channel or resource not found
</ResponseField>

## WebSocket Gateway

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

### Client Connection

```javascript theme={null}
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:

```javascript theme={null}
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

## Related Modules

<Card title="Agent Module" icon="robot" href="/api/agent">
  Agent orchestration and message processing
</Card>

<Card title="Tools Module" icon="wrench" href="/api/tools">
  Tools that agents can use to respond to messages
</Card>
