# How to Use iroh from Non-Rust Languages via iroh-ffi: A Complete Guide

> Integrate iroh's P2P networking into Python Go C and more using iroh-ffi. This guide shows how to leverage iroh from non Rust languages for seamless C compatible API integration.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-07-14

---

**You can use iroh-ffi to expose iroh's encrypted peer-to-peer networking through a C-compatible API, enabling integration with Python, Go, C, and other languages via shared libraries and language-specific bindings.**

The **iroh** library from n0-computer/iroh provides high-performance, encrypted networking primitives written in Rust. To make these capabilities available to developers working in other programming languages, the project maintains **iroh-ffi**, a dedicated crate that wraps the core Rust API in a stable C interface.

## Architecture of iroh-ffi

The integration follows a three-layer architecture that keeps security-critical code in Rust while exposing minimal, language-agnostic interfaces.

**The core layer** (`iroh` crate) implements the full protocol stack, including QUIC transport, relay handling, DNS resolution, and the high-level `Endpoint` API. This code resides in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) and [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs).

**The FFI layer** (`iroh-ffi` crate) provides a thin C-compatible wrapper around the core API. It translates Rust types into opaque pointers and simple structs that any language with FFI support can consume. According to the source code in [`iroh-ffi/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-ffi/src/lib.rs), this layer deliberately exposes only essential operations: creating an `Endpoint`, opening streams, sending/receiving bytes, and shutdown.

**Language bindings** consist of small glue code that loads the shared library and forwards calls to the C API. This might be `extern "C"` declarations in C++, `ctypes` definitions in Python, or `cgo` imports in Go.

The data flow follows this pattern:

```

Your Program → Language Binding → iroh-ffi (C API) → iroh Core (Rust)

```

Because the API uses only stable C types (pointers, integers, and byte buffers), the same shared library works across Windows, macOS, and Linux without modification.

## Building the iroh-ffi Shared Library

To begin using iroh from non-Rust languages, you must first compile the FFI crate as a dynamic library.

1. **Clone the iroh-ffi repository** (maintained separately from the main iroh repo):

   ```bash
   git clone https://github.com/n0-computer/iroh-ffi
   cd iroh-ffi
   ```

2. **Compile the shared library** using Cargo. The [`iroh-ffi/Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/iroh-ffi/Cargo.toml) defines a `cdylib` target, so the build produces platform-specific binaries:

   ```bash
   cargo build --release
   ```

   This creates `target/release/libiroh.so` (Linux), `target/release/libiroh.dylib` (macOS), or `target/release/iroh.dll` (Windows).

3. **Locate the header file** at [`iroh-ffi/include/iroh.h`](https://github.com/n0-computer/iroh/blob/main/iroh-ffi/include/iroh.h), which declares the C symbols for your target language to import.

## Using iroh-ffi from C

The C integration requires including the header and linking against the shared library. The pattern follows: initialize a runtime, create an endpoint, open a bidirectional stream, then read/write data.

```c
#include "iroh.h"
#include <string.h>
#include <stdio.h>

int main() {
    // Initialize the runtime (handles async execution)
    IrohRuntime *rt = iroh_runtime_new();

    // Create an endpoint with default configuration
    IrohEndpoint *ep = iroh_endpoint_new(rt, NULL);

    // Open a bidirectional stream to a remote peer
    IrohStream *stream = iroh_endpoint_open_bi(ep, "iroh://<remote_hash>");

    // Send payload
    const char *msg = "hello from C";
    iroh_stream_write(stream, (const uint8_t *)msg, strlen(msg));

    // Receive response (blocking read)
    uint8_t buf[256];
    size_t n = iroh_stream_read(stream, buf, sizeof(buf));
    buf[n] = '\0';
    printf("remote said: %s\n", buf);

    // Cleanup resources
    iroh_stream_close(stream);
    iroh_endpoint_close(ep);
    iroh_runtime_free(rt);
    return 0;
}

```

Key functions include `iroh_runtime_new()` for initialization, `iroh_endpoint_open_bi()` for establishing connections, and `iroh_stream_read()`/`iroh_stream_write()` for data transfer.

## Using iroh-ffi from Python

Python accesses the library through `ctypes`, loading the shared library and explicitly defining argument and return types for type safety.

```python
import ctypes
from pathlib import Path

# Load the compiled library

lib = ctypes.CDLL(Path("target/release/libiroh.so").as_posix())

# Declare function signatures matching iroh.h

lib.iroh_runtime_new.restype = ctypes.c_void_p
lib.iroh_endpoint_new.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
lib.iroh_endpoint_new.restype = ctypes.c_void_p
lib.iroh_endpoint_open_bi.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
lib.iroh_endpoint_open_bi.restype = ctypes.c_void_p
lib.iroh_stream_write.argtypes = [ctypes.c_void_p,
                                  ctypes.POINTER(ctypes.c_uint8),
                                  ctypes.c_size_t]
lib.iroh_stream_read.argtypes = [ctypes.c_void_p,
                                 ctypes.POINTER(ctypes.c_uint8),
                                 ctypes.c_size_t]
lib.iroh_stream_read.restype = ctypes.c_size_t

# Initialize

rt = lib.iroh_runtime_new()
ep = lib.iroh_endpoint_new(rt, None)

# Connect

stream = lib.iroh_endpoint_open_bi(ep, b"iroh://<remote_hash>")

# Send data

msg = b"hello from Python"
buf = (ctypes.c_uint8 * len(msg)).from_buffer_copy(msg)
lib.iroh_stream_write(stream, buf, len(msg))

# Receive data

out = (ctypes.c_uint8 * 256)()
n = lib.iroh_stream_read(stream, out, 256)
print("remote said:", bytes(out[:n]).decode())

# Cleanup

lib.iroh_stream_close(stream)
lib.iroh_endpoint_close(ep)
lib.iroh_runtime_free(rt)

```

This approach requires manually mapping C types to Python ctypes, but provides full access to the underlying `iroh_endpoint_open_bi()` and stream operations without native Python extensions.

## Using iroh-ffi from Go

Go utilizes `cgo` to import the C headers and link against the shared library, using `unsafe.Pointer` for buffer operations.

```go
// #cgo LDFLAGS: -L${SRCDIR}/target/release -liroh
// #include "iroh.h"
import "C"
import (
    "unsafe"
)

func main() {
    // Initialize runtime
    rt := C.iroh_runtime_new()

    // Create endpoint (nil uses default config)
    ep := C.iroh_endpoint_new(rt, nil)

    // Open bidirectional stream
    remote := C.CString("iroh://<remote_hash>")
    defer C.free(unsafe.Pointer(remote))
    stream := C.iroh_endpoint_open_bi(ep, remote)

    // Send message
    msg := []byte("hello from Go")
    C.iroh_stream_write(stream,
        (*C.uint8_t)(unsafe.Pointer(&msg[0])),
        C.size_t(len(msg)))

    // Read response
    buf := make([]byte, 256)
    n := C.iroh_stream_read(stream,
        (*C.uint8_t)(unsafe.Pointer(&buf[0])),
        C.size_t(len(buf)))
    println("remote said:", string(buf[:n]))

    // Teardown
    C.iroh_stream_close(stream)
    C.iroh_endpoint_close(ep)
    C.iroh_runtime_free(rt)
}

```

The `cgo` directives link against `libiroh` while the Go code manages the lifecycle of opaque pointers returned by `iroh_runtime_new()` and `iroh_endpoint_new()`.

## Core API Patterns Across Languages

Regardless of language, using iroh via FFI follows a consistent lifecycle pattern:

1. **Initialize the runtime** with `iroh_runtime_new()` — this creates the async execution context required by the underlying Rust code.
2. **Create an endpoint** using `iroh_endpoint_new()` — this configures the local peer identity and network stack.
3. **Open streams** via `iroh_endpoint_open_bi()` — establishes bidirectional QUIC streams to remote endpoints identified by their iroh URL.
4. **Transfer data** using `iroh_stream_write()` and `iroh_stream_read()` — both functions handle raw byte buffers and return counts of bytes processed.
5. **Cleanup** by calling `iroh_stream_close()`, `iroh_endpoint_close()`, and `iroh_runtime_free()` in reverse order to prevent resource leaks.

For reference implementations demonstrating these patterns in pure Rust, consult [`iroh/examples/connect.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/connect.rs) in the main repository.

## Summary

- **iroh-ffi** provides a C-compatible wrapper around the Rust iroh networking stack, located in [`iroh-ffi/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-ffi/src/lib.rs) and [`iroh-ffi/include/iroh.h`](https://github.com/n0-computer/iroh/blob/main/iroh-ffi/include/iroh.h).
- Build the shared library using `cargo build --release` to generate `libiroh.so`, `iroh.dll`, or `libiroh.dylib` depending on your platform.
- The API centers on three opaque types: `IrohRuntime`, `IrohEndpoint`, and `IrohStream`, managed through explicit lifecycle functions.
- All encryption and protocol logic remains in the Rust core ([`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)), ensuring security regardless of the calling language.
- Integration requires only standard FFI mechanisms available in C, Python (ctypes), Go (cgo), and most other systems languages.

## Frequently Asked Questions

### What languages are supported by iroh-ffi?

Any language capable of calling C functions through FFI can use iroh-ffi. The repository provides tested examples for C, Python (via ctypes), and Go (via cgo), but the underlying C API in [`iroh-ffi/include/iroh.h`](https://github.com/n0-computer/iroh/blob/main/iroh-ffi/include/iroh.h) enables bindings for Node.js (N-API), Java (JNI), C#, and others.

### Where does the encryption logic reside when using iroh-ffi?

All cryptographic operations, QUIC handshakes, and protocol implementations remain in the core Rust library ([`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) and [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)). The FFI layer only forwards calls to these Rust functions and translates data types, ensuring that security-critical code stays in memory-safe Rust regardless of the calling language.

### How do I handle memory management when using the C API?

The API follows explicit resource management: you must call `iroh_runtime_free()`, `iroh_endpoint_close()`, and `iroh_stream_close()` to release resources. The opaque pointers returned by initialization functions (e.g., `iroh_runtime_new()`) are owned by the caller and must be freed to prevent memory leaks, as the Rust side does not automatically clean up when the calling process drops references.

### Can I use iroh-ffi on Windows, macOS, and Linux?

Yes. The [`iroh-ffi/Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/iroh-ffi/Cargo.toml) defines a `cdylib` crate type that compiles to platform-specific dynamic libraries (`.so`, `.dylib`, `.dll`) from the same source code. Because the C API uses only primitive types and opaque pointers defined in [`iroh.h`](https://github.com/n0-computer/iroh/blob/main/iroh.h), the generated library is portable across all three platforms without modification to your application code.