# Understanding the TinyBus ABI for Loadable Native Modules in OpenHuman

> Explore the TinyBus ABI, a versioned interface enabling OpenHuman to load native extensions as cdylib modules. Discover its D-Bus-style object paths, method signatures, manifest verification, and no-unload guarantee.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: internals
- Published: 2026-08-24

---

**The TinyBus ABI is a lightweight, versioned interface that allows OpenHuman to load native extensions as compiled `cdylib` modules, exposing D-Bus-style object paths and method signatures with mandatory manifest verification and a strict no-unload guarantee.**

OpenHuman extends its capabilities through native modules that communicate via TinyBus, an internal message bus system. The **TinyBus ABI for loadable native modules** defines the contract between the core runtime and these external libraries, specifying how functions are exported, how errors are handled, and how modules are attested before execution.

## Core Concepts of the TinyBus Module ABI

A TinyBus module is not a simple DLL; it is a compiled `cdylib` that implements a specific protocol for service discovery, method invocation, and lifecycle management.

### Module Definition and Lifetime

According to [`src/openhuman/modules/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/mod.rs), a module is defined as "a compiled `cdylib` that speaks the tinybus module ABI"【src/openhuman/modules/mod.rs†L3-L5】. Once loaded, this library remains mapped in memory for the entire process lifetime. The source explicitly states that "tinybus never unloads a library"【src/openhuman/modules/mod.rs†L24-L25】, meaning a faulted or compromised module is permanently disabled rather than removed from the address space.

### Object Path Conventions

Modules expose services under unique bus names (e.g., `ai.tinyhumans.tinymemory.ChatHost`). TinyBus derives the object path by transforming the bus name: replacing dot separators with forward slashes. As documented in [`src/openhuman/modules/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/registry.rs), "tinybus derives a module's object path from its bus name by replacing" dots with slashes【src/openhuman/modules/registry.rs†L787-L788】. For example, the bus name `ai.tinyhumans.tinymemory.ChatHost` becomes the object path `/ai/tinyhumans/tinymemory/ChatHost`.

### Interface Declaration

Developers export functions using the `#[tinybus::interface(name = "...")]` attribute macro. This attribute expands to register D-Bus-compatible method signatures on the module's object path. In [`src/openhuman/modules/tokenjuice_host.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/tokenjuice_host.rs), the interface is declared as:

```rust
#[tinybus::interface(name = "ai.tinyhumans.tinyjuice.MlHost")]
pub trait MlHost {
    // method definitions
}

```

【src/openhuman/modules/tokenjuice_host.rs†L15-L21】

### Error Handling Contract

All exported methods must return `tinybus::Result<T>`. Errors are wrapped in `tinybus::Error::MethodFailed` with sanitized, non-secret messages to prevent information leakage. The helper function `method_error` is used throughout implementations to convert internal errors into the required format【src/openhuman/modules/tokenjuice_host.rs†L29-L30】.

## Module Lifecycle and Security

The TinyBus ABI enforces strict security boundaries during the loading process, ensuring only authenticated code executes within the core process.

### Manifest Verification

Before admission, TinyBus fetches the release's [`checksum.toml`](https://github.com/tinyhumansai/openhuman/blob/main/checksum.toml) and compares it against a pinned digest stored in the registry. As noted in [`src/openhuman/modules/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/registry.rs), "tinybus fetches the release's own [`checksum.toml`](https://github.com/tinyhumansai/openhuman/blob/main/checksum.toml), compares it with the digest" to verify integrity【src/openhuman/modules/registry.rs†L14-L15】. Only modules with matching digests are loaded via `dlopen`.

### Proxy Acquisition

Host code interacts with loaded modules through a `tinybus::Proxy` object. Once attestation completes successfully, the registry creates and returns this proxy. In [`src/openhuman/modules/wallet.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/wallet.rs), the `attested_proxy` method returns a `tinybus::Proxy` that core components use to invoke module methods【src/openhuman/modules/wallet.rs†L247-L252】.

### No-Unload Guarantee

The ABI deliberately omits an unload operation. As stated in [`src/openhuman/modules/schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/schemas.rs): "There is no `unload`, and there cannot be: tinybus never unloads a library"【src/openhuman/modules/schemas.rs†L7-L8】. This design prevents use-after-free vulnerabilities and ensures that a faulted module leaves the process in a consistent, predictable state.

## Implementing a TinyBus Module

To create a compliant native module, you must implement the trait defined by your interface attribute and handle the TinyBus result types.

**Defining the interface:**

```rust
#[tinybus::interface(name = "ai.tinyhumans.tinymemory.ChatHost")]
pub trait ChatHost {
    /// Generate a model response for a chat message.
    async fn generate(
        &self,
        prompt: String,
        temperature: f32,
    ) -> tinybus::Result<ModelResponse>;
}

```

**Implementing the trait:**

```rust
pub struct ChatHostImpl;

#[tinybus::impl_interface]
impl ChatHost for ChatHostImpl {
    async fn generate(
        &self,
        prompt: String,
        temperature: f32,
    ) -> tinybus::Result<ModelResponse> {
        // Implementation logic here
        Ok(ModelResponse { text: response })
    }
}

```

**Loading from the core:**

```rust
use tinybus::{Connection, Proxy};

async fn load_chat_module(conn: &Connection) -> Result<Proxy, tinybus::Error> {
    // Query registry for the bus name implementing this interface
    let bus_name = conn.call("modules", "list", ("ai.tinyhumans.tinymemory.ChatHost",))
        .await?
        .0;

    // Create proxy using the derived object path
    let object_path = format!("/{}", bus_name.replace('.', "/"));
    let proxy = tinybus::Proxy::new(conn, &bus_name, &object_path).await?;
    
    Ok(proxy)
}

```

**Invoking methods:**

```rust
async fn generate_chat(
    proxy: &Proxy,
    prompt: &str,
) -> Result<ModelResponse, tinybus::Error> {
    proxy.call("generate", (prompt.to_string(), 0.7_f32)).await
}

```

## Summary

- The **TinyBus ABI** requires modules to be compiled as `cdylib` libraries that implement D-Bus-style interfaces using the `#[tinybus::interface]` attribute.
- **Object paths** are automatically derived from bus names by replacing dots with slashes, as implemented in [`src/openhuman/modules/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/registry.rs).
- **Security** depends on manifest verification against pinned digests in [`checksum.toml`](https://github.com/tinyhumansai/openhuman/blob/main/checksum.toml) before any library code executes.
- **Error handling** mandates `tinybus::Result<T>` return types and sanitized error messages via `method_error`.
- **Lifetime management** follows a strict no-unload policy: once loaded in [`src/openhuman/modules/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/mod.rs), a library remains mapped until process termination, preventing state corruption.

## Frequently Asked Questions

### What is the TinyBus ABI?

The TinyBus ABI is the application binary interface that defines how OpenHuman loads and communicates with native extension modules. It specifies that modules must be compiled as `cdylib` libraries, export methods using the `#[tinybus::interface]` attribute, return `tinybus::Result<T>` types, and identify themselves via unique bus names that map to D-Bus-style object paths.

### How does TinyBus handle module verification?

Before loading any native code, TinyBus performs attestation by downloading the module's [`checksum.toml`](https://github.com/tinyhumansai/openhuman/blob/main/checksum.toml) manifest and comparing its digest against a pinned value in the registry, as defined in [`src/openhuman/modules/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/registry.rs). Only modules with matching cryptographic digests are loaded via `dlopen`, ensuring code integrity.

### Why doesn't TinyBus support unloading modules?

TinyBus intentionally omits unload functionality because unloading a shared library that has potentially faulted or been compromised could leave the host process in an inconsistent state. As documented in [`src/openhuman/modules/schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/schemas.rs), this guarantees that once a module is refused or fails, it remains unavailable for the lifetime of the process, preventing use-after-free vulnerabilities.

### How are object paths determined in TinyBus?

Object paths are derived automatically from the module's bus name by replacing period separators with forward slashes. For example, a module registering under the bus name `ai.tinyhumans.tinymemory.ChatHost` receives the object path `/ai/tinyhumans/tinymemory/ChatHost`, as implemented in the registry's path derivation logic.