Understanding OpenHuman’s Loadable Native Module System and TinyBus ABI Admission

OpenHuman’s loadable native module system is a security-hardened extension mechanism that downloads, verifies, and dynamically loads cdylib artifacts over TinyBus, ensuring only cryptographically signed binaries that match hard-coded SHA-256 digests execute within the host process.

OpenHuman extends its capabilities without recompiling the entire application through a sophisticated native module system located in src/openhuman/modules/. Each module is a compiled cdylib that implements the TinyBus ABI—a lightweight, version-checked RPC protocol that governs all inter-module communication. This architecture enables rapid feature expansion while maintaining strict security boundaries through cryptographic admission controls.

How the Module Registry Works

OpenHuman employs a compiled-in registry pattern that prevents supply-chain attacks by treating the list of admissible modules as immutable source code rather than runtime configuration.

The Compiled Module Registry

The admission process begins in src/openhuman/modules/registry.rs, which exports a static const table named MODULES. This table enumerates every module permitted to load, encoding not just metadata but security parameters:

// src/openhuman/modules/registry.rs
pub const MODULES: &[ModuleInfo] = &[
    ModuleInfo {
        name: "tinydocs",
        version: "0.5.0",
        url: "https://example.com/tinydocs-0.5.0.tar.gz",
        sha256: "3a5f7c9e…", // hard-coded digest
    },
    // …other entries…
];

Each ModuleInfo struct contains the module’s canonical name, expected semantic version, remote artifact URL, and a SHA-256 digest of the published binary. Because this registry is compiled into the final binary, adversaries cannot inject malicious modules through server-side manipulation—the host will only accept a module whose cryptographic fingerprint matches the hard-coded entry exactly.

SHA-256 Verification and Admission

When the core requires a module, src/openhuman/modules/loader.rs orchestrates the admission workflow:

  1. Cache Check – Verify if the artifact exists in local storage.
  2. Download – Fetch the artifact from the registry URL if missing.
  3. Digest Verification – Compute the SHA-256 hash of the downloaded file and compare it against the sha256 field in MODULES.
  4. Admission Decision – Abort loading with an error if digests differ; proceed to dynamic loading on match.

This verification step constitutes the TinyBus ABI admission process, ensuring that the exact binary the developers shipped is the only code executed.

Dynamic Loading and TinyBus Integration

Once verified, modules transition from static artifacts to active runtime components through Rust’s libloading crate and TinyBus broker initialization.

The dlopen Flow

The core loading logic in src/openhuman/modules/mod.rs handles the transition from verified file to loaded library:

// src/openhuman/modules/mod.rs
fn load_module(name: &str) -> anyhow::Result<Arc<dyn TinyBusInterface>> {
    // 1️⃣ Find the entry in the compiled registry
    let info = MODULES.iter().find(|m| m.name == name)
        .ok_or_else(|| anyhow!("module not registered"))?;

    // 2️⃣ Download and verify the artifact (omitted for brevity)

    // 3️⃣ dlopen the library
    let lib = unsafe { Library::new(&info.path)? };

    // 4️⃣ Resolve the Tiny Bus entry point
    let ctor: Symbol<extern "C" fn() -> *mut dyn TinyBusInterface> =
        unsafe { lib.get(b"tinybus_entrypoint")? };
    let iface = unsafe { Arc::from_raw(ctor()) };

    // 5️⃣ Register the broker
    let broker = OnceBus::init_in_process();
    broker.register_interface(iface.clone());

    Ok(iface)
}

The module must expose a C-compatible symbol named tinybus_entrypoint that returns a pointer to an implementation of TinyBusInterface. The host uses dlopen (via libloading) to map the library into the process address space, then resolves this symbol to instantiate the module’s RPC interface.

TinyBus Broker Initialization

After loading, the core creates a TinyBus broker using OnceBus::init_in_process(). This broker manages the module’s BUS_NAME and OBJECT_PATH, routing messages through in-process channels that enforce deadlines and bounded buffers. The broker registration in src/openhuman/core/all.rs wires the module’s RPC objects into the host’s central dispatcher, making module-provided tools available to the rest of the application.

Contract-Only Bus Crates and Type Safety

OpenHuman prevents ABI drift through a compile-time contract system using -bus crates. Every domain supporting native modules distributes a corresponding crate (e.g., tinydocs-bus, tinywallet-bus) that contains only type definitions, constant names, and the ABI version.

// vendor/tinydocs-bus/crates/tinydocs-bus/src/lib.rs
pub const BUS_NAME: &str = "ai.tinyhumans.tinydocs";
pub const OBJECT_PATH: &str = "/ai.tinyhumans.tinydocs/1";
pub const ABI_VERSION: u32 = 1;

// Types used on the bus
#[derive(Serialize, Deserialize)]
pub struct GenerateDocumentInput {
    pub spec: DocumentSpec,
}

Both the host and module depend on the same -bus crate, ensuring that any change to method signatures or payload structures produces a compile-time error rather than a runtime failure. This deterministic ABI guarantee eliminates version-mismatch bugs common in traditional plugin architectures.

Isolation and Failure Handling

While modules share the host’s address space, OpenHuman isolates failures through the TinyBus broker’s message queues rather than process boundaries.

Runtime Safety Guarantees

The broker enforces zero-copy messaging, timeouts, and back-pressure on all module interactions. If a module panics or exceeds its deadline, the broker captures the error and marks the module as failed:

match result {
    Ok(doc) => println!("Generated: {}", doc.id),
    Err(e) if e.is_timeout() => {
        log::error!("Module timed out – marking as failed");
        // The broker will automatically drop the module until the process restarts
    }
    Err(e) => log::error!("Module error: {}", e),
}

Once failed, a module remains in that state until the host process restarts; the core never attempts to reload or reinitialize a crashed module during a single runtime session. This fail-stop behavior protects the host from cascading failures in third-party extensions.

Summary

  • Immutable Registry: The MODULES constant in src/openhuman/modules/registry.rs acts as a cryptographic allow-list, binding specific SHA-256 digests to module names at compile time.
  • Cryptographic Admission: The TinyBus ABI admission process verifies downloaded artifacts against hard-coded hashes before executing dlopen, preventing supply-chain attacks.
  • Type-Safe Contracts: -bus crates provide compile-time guarantees that host and module agree on RPC signatures, eliminating version drift.
  • Brokered Isolation: TinyBus brokers enforce message deadlines and buffer limits, containing module failures without process isolation overhead.
  • Deterministic Lifecycle: Modules are loaded once, verified, and never reloaded after failure, ensuring predictable runtime behavior.

Frequently Asked Questions

What is the TinyBus ABI in OpenHuman?

The TinyBus ABI is a lightweight, version-checked RPC protocol that defines how OpenHuman’s core communicates with loadable native modules. It specifies standard interface names, object paths, and messaging semantics that allow the host to call into dynamically loaded cdylib binaries through in-process channels without serialization overhead.

How does OpenHuman prevent malicious modules from loading?

OpenHuman prevents malicious loading through the admission process in src/openhuman/modules/registry.rs, which hard-codes the SHA-256 digest of every permitted module. During loading, the core downloads the artifact, computes its hash, and aborts if the digest does not match the compiled-in value. This ensures only cryptographically verified binaries execute, even if the distribution server is compromised.

Why does OpenHuman use contract-only -bus crates instead of shared headers?

Contract-only -bus crates provide compile-time type safety across the host-module boundary. By importing the same Rust crate that defines the ABI_VERSION, BUS_NAME, and payload structs, both sides guarantee binary compatibility at compile time. If either side changes a method signature, the Rust compiler raises an error, preventing runtime ABI mismatches that plague traditional C-based plugin systems.

What happens when a native module crashes or times out?

When a module crashes or exceeds its TinyBus deadline, the broker in src/openhuman/modules/mod.rs catches the failure and marks the module as failed. The host immediately ceases all communication with that module and does not attempt recovery or reloading until the next process restart. This fail-stop design isolates faults while maintaining system stability.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →