Skip to content
Back to skills

Using Iii

ASecurity

``` </Tab> <Tab title="Rust"> ```rust let channel = iii.create_channel(None).await?; // channel.writer // channel.reader // channel.writer_ref // channel.reader_ref ``` </Tab> </Tabs> Write the stream payload to the local writer and close it when you are done. <Tabs> <Tab title="Node / TypeScript"> ```typescript const channel = await iii.createChannel(); channel.writer.stream.end(Buffer.from("file contents")); ``` </Tab> <Tab title="Python"> ```python channel = await iii_client.create_channel...

  • 18,821 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 3, 2026
developmenttypescriptpythonrustgonode

Works with

  • cli

Security analysis

A100/100

Pro scans all 19 files and shows the line behind each finding

Scanned September 3, 2026

npx -y skills add iii-hq/iii --skill using-iii --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Using Iii?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Using Iii
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/iii-hq-using-iii/badge)](https://www.skillsdirectory.com/skills/iii-hq-using-iii)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
<!-- generated by iii-skill-render. DO NOT EDIT (changes here are overwritten on the next render). Edit docs/0-12-0/using-iii/channels.mdx. -->


## Goal

Stream a large or binary payload from one function to another without putting the data itself in a
JSON function payload.

Use a normal function invocation when the payload is small JSON and can be handled as one request.
Use a channel when the payload is large, binary, or naturally stream-shaped.

Good fits for channels:

- File uploads and downloads.
- Images, audio, video, PDFs, and datasets.
- Progress updates during long-running work.
- Producer and consumer pipelines where the data should move as a stream.

<Info title="Architecture">
  For the underlying model, see [Channels architecture](../understanding-iii/channels).
</Info>

## Steps

### 1. Create a channel

A channel has two local stream objects and two serializable refs:

- `writer`: local writable stream.
- `reader`: local readable stream.
- `writerRef`: serializable token for the writer end.
- `readerRef`: serializable token for the reader end.

<Tabs>
  <Tab title="Node / TypeScript">
    ```typescript
    const channel = await iii.createChannel();

    // channel.writer
    // channel.reader
    // channel.writerRef
    // channel.readerRef
    ```
  </Tab>
  <Tab title="Python">
    ```python
    channel = iii_client.create_channel()

    # channel.writer
    # channel.reader
    # channel.writer_ref
    # channel.reader_ref
    ```
  </Tab>
  <Tab title="Rust">
    ```rust
    let channel = iii.create_channel(None).await?;

    // channel.writer
    // channel.reader
    // channel.writer_ref
    // channel.reader_ref
    ```
  </Tab>
</Tabs>

### 2. Write to the channel

Write the stream payload to the local writer and close it when you are done.

<Tabs>
  <Tab title="Node / TypeScript">
    ```typescript
    const channel = await iii.createChannel();

    channel.writer.stream.end(Buffer.from("file contents"));
    ```
  </Tab>
  <Tab title="Python">
    ```python
    channel = await iii_client.create_channel_async()

    await channel.writer.write(b"file contents")
    await channel.writer.close_async()
    ```
  </Tab>
  <Tab title="Rust">
    ```rust
    let channel = iii.create_channel(None).await?;

    channel.writer.write(b"file contents").await?;
    channel.writer.close().await?;
    ```
  </Tab>
</Tabs>

### 3. Pass the reader ref to another function

Pass the `readerRef` / `reader_ref` as part of a normal function invocation. The receiving function
uses that ref to read from the channel.

<Tabs>
  <Tab title="Node / TypeScript">
    ```typescript
    const result = await iii.trigger({
      function_id: "files::process",
      payload: {
        filename: "report.csv",
        reader: channel.readerRef,
      },
    });
    ```
  </Tab>
  <Tab title="Python">
    ```python
    result = await iii_client.trigger_async({
        "function_id": "files::process",
        "payload": {
            "filename": "report.csv",
            "reader": channel.reader_ref.model_dump(),
        },
    })
    ```
  </Tab>
  <Tab title="Rust">
    ```rust
    use iii_sdk::TriggerRequest;
    use serde_json::json;

    let result = iii
        .trigger(TriggerRequest {
            function_id: "files::process".to_string(),
            payload: json!({
                "filename": "report.csv",
                "reader": channel.reader_ref,
            }),
            action: None,
            timeout_ms: None,
        })
        .await?;
    ```
  </Tab>
</Tabs>

### 4. Read from the channel

Node and Python deserialize channel refs into live channel objects before your handler runs. Rust
receives the ref in JSON and reconstructs the reader with `ChannelReader::new(...)`.

<Tabs>
  <Tab title="Node / TypeScript">
    ```typescript
    import type { ChannelReader } from "iii-sdk";

    worker.registerFunction("files::process", async (input: { reader: ChannelReader }) => {
      let bytes = 0;

      for await (const chunk of input.reader.stream) {
        bytes += Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk);
      }

      return { bytes };
    });
    ```
  </Tab>
  <Tab title="Python">
    ```python
    async def process_file(input: dict) -> dict:
        reader = input["reader"]
        chunks = []

        async for chunk in reader:
            chunks.append(chunk)

        return {"bytes": sum(len(chunk) for chunk in chunks)}

    worker.register_function("files::process", process_file)
    ```
  </Tab>
  <Tab title="Rust">
    ```rust
    use iii_sdk::{ChannelDirection, ChannelReader, IIIError};
    use serde_json::json;

    let refs = iii_sdk::extract_channel_refs(&input);
    let reader_ref = refs
        .iter()
        .find(|(key, ref_)| key == "reader" && matches!(ref_.direction, ChannelDirection::Read))
        .map(|(_, ref_)| ref_.clone())
        .ok_or_else(|| IIIError::Handler("missing reader channel ref".to_string()))?;

    let reader = ChannelReader::new(iii.address(), &reader_ref);
    let mut bytes = 0;

    while let Some(chunk) = reader.next_binary().await? {
        bytes += chunk.len();
    }

    Ok(json!({ "bytes": bytes }))
    ```
  </Tab>
</Tabs>

## Result

The caller passes only a small ref through `trigger()`. The stream payload travels over the channel,
and the receiving function reads it incrementally.

## Related pages

- [Channels architecture](../understanding-iii/channels)
- [Node SDK channels](../sdk-reference/node-sdk#channels)
- [Python SDK channels](../sdk-reference/python-sdk#channels)
- [Rust SDK channels](../sdk-reference/rust-sdk#channels)

Files in this skill

  • channels.mdx5.5 KB
  • channels.mdx.skill.md5.5 KB
  • cli.mdx2.8 KB
  • cli.mdx.skill.md2.8 KB
  • console.mdx7.4 KB
  • console.mdx.skill.md7.4 KB
  • deployment.mdx3.2 KB
  • deployment.mdx.skill.md3.2 KB
  • engine.mdx2.5 KB
  • engine.mdx.skill.md2.3 KB
  • functions.mdx2.9 KB
  • functions.mdx.skill.md2.9 KB
  • triggers.mdx5.8 KB
  • triggers.mdx.skill.md5.8 KB
  • workers-registry.mdx1.6 KB
  • workers-registry.mdx.skill.md1.7 KB
  • workers.mdx6.9 KB
  • workers.mdx.skill.md6.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.