How Loadable Native Modules Work with TinyBus ABI, Digest Gates, and Module Hosts in OpenHuman

OpenHuman employs a two-gate security architecture where a compiled-in registry validates module identities and SHA-256 digests verify binary integrity before loading native cdylib code through the tinybus ABI.

The tinyhumansai/openhuman repository implements a runtime plug-in system for loadable native modules that extends core functionality without recompiling the host binary. This architecture uses domain-specific hosts like tinydocs and tinymemory to manage capabilities such as document synthesis and memory recall, all communicating through a lightweight RPC layer called the tinybus ABI.

The Two-Gate Security Model

OpenHuman protects against unauthorized native code execution through complementary verification layers defined in src/openhuman/modules/registry.rs and src/openhuman/modules/types.rs.

The Compiled-In Registry Gate

The first gate is a hard-coded table of known modules stored as pub const ALL: &[ModuleRecord] at lines 49-61 of src/openhuman/modules/registry.rs. When the core attempts to load a capability, it calls find("tinydocs") to retrieve a ModuleRecord. If the module ID does not exist in this compiled-in list, the loading process aborts immediately, ensuring only explicitly approved modules can be instantiated.

The Digest Verification Gate

The second gate verifies binary integrity through SHA-256 hashes. Each ModuleRecord contains an assets: &'static [PlatformAsset] array where every entry includes a sha256: &'static str field. Before calling dlopen, the core downloads the archive from release_url, computes its SHA-256 hash, and compares it against the registry value. Tests such as tests::every_digest_is_a_lowercase_sha256 at lines 200-207 enforce correct hash formatting.

Registry Structure and Module Metadata

Each module is defined by the ModuleRecord struct in src/openhuman/modules/types.rs:

pub struct ModuleRecord {
    pub id: &'static str,
    pub description: &'static str,
    pub bus_name: &'static str,
    pub object_path: &'static str,
    pub version: &'static str,
    pub release_url: &'static str,
    pub assets: &'static [PlatformAsset],
    pub load: LoadPolicy,
}

The PlatformAsset struct specifies platform-specific binaries:

pub struct PlatformAsset {
    pub host_key: &'static str,
    pub archive: &'static str,
    pub sha256: &'static str,
}

The host_key (e.g., ubuntu-22.04-x86_64, macos-15-arm64) is generated by modules::platform::candidates_for(os, arch, glibc) and must match the current host environment exactly. The load field uses the LoadPolicy enum to specify Lazy or Eager initialization behavior.

The Module Loading Lifecycle

When the core requires a native capability, it executes a five-phase workflow orchestrated through src/openhuman/modules/host.rs and the tinybus crate:

  1. Resolve the record: Call find("tinydocs") to retrieve the ModuleRecord from the compiled registry.
  2. Select platform asset: Use record.asset_for(&host_key) where host_key is obtained from modules::platform::current_host_key().
  3. Download and verify: Fetch the archive from release_url and validate against asset.sha256.
  4. Create proxy: Load the shared library via dlopen and instantiate tinybus::Proxy::new(&record.bus_name, &record.object_path).
  5. Expose API: Use the module's contract crate (e.g., tinydocs_bus::Documents) to interact with the loaded binary.
use openhuman::modules::{find, LoadPolicy};
use tinybus::Proxy;

// Resolve registry entry
let record = find("tinydocs").expect("module known");

// Select asset for current host
let host_key = modules::platform::current_host_key();
let asset = record.asset_for(&host_key).expect("supported host");

// Verify digest and create proxy (tinybus handles verification internally)
let proxy = Proxy::new(&record.bus_name, &record.object_path)
    .expect("failed to create tinybus proxy");

// Use the module API
use tinydocs_bus::Documents;
let client = Documents::new(proxy);
let doc = client.generate_docx(...).await?;

Attested Proxies for Sensitive Operations

Modules handling sensitive data, such as tinywallet with recovery phrases, use additional verification through attested_proxy defined at lines 247-254 of src/openhuman/modules/wallet.rs. This function performs digest verification before creating the tinybus proxy, ensuring cryptographic material is only transmitted to binaries matching the pinned SHA-256 digests in the registry.

use openhuman::modules::wallet::attested_proxy;
use openhuman::config::Config;

let config = Config::load_or_init().await?;
let proxy = attested_proxy(&config).await?;

use tinywallet_bus::Wallet;
let wallet = Wallet::new(proxy);
let address = wallet.get_address(...).await?;

Lazy vs. Eager Loading Policies

The LoadPolicy enum in src/openhuman/modules/types.rs determines when native code is fetched and loaded:

Lazy modules (tinydocs, tinywallet, tinyvoice, tinyjuice, tinymcp) are downloaded and loaded only when a user invokes a feature requiring that capability. This reduces startup time and bandwidth for unused features.

Eager modules (tinymemory) are loaded at startup because the core's RPC surface and agent tool list depend on the memory engine's capabilities. The registry marks these with load: LoadPolicy::Eager, causing src/openhuman/modules/ops.rs to initialize them during the core boot sequence.

Module Hosts and Core Integration

Each native module is wrapped by a host implementation in src/openhuman/modules/<module>.rs (e.g., memory.rs, documents.rs). These hosts abstract the tinybus proxy and are responsible for:

  • Selecting the correct PlatformAsset via the registry
  • Verifying the SHA-256 digest before library loading
  • Creating the tinybus::Proxy and exposing the contract-crate API

The generic host logic resides in src/openhuman/modules/host.rs, while src/openhuman/modules/ops.rs provides the glue code that integrates these hosts into the core's initialization flow, handling both lazy and eager loading policies according to the registry configuration.

Summary

  • Two-gate security: A compiled-in registry at src/openhuman/modules/registry.rs validates module identity, while SHA-256 digests in PlatformAsset ensure binary integrity before execution.
  • TinyBus ABI: Provides the RPC surface between the core and loaded cdylib binaries through tinybus::Proxy.
  • Load policies: Lazy modules (like tinydocs) load on-demand, while Eager modules (like tinymemory) initialize at startup to populate core capabilities.
  • Attested proxies: Sensitive modules use attested_proxy to verify digests before exposing cryptographic interfaces such as wallet recovery phrases.

Frequently Asked Questions

What is the tinybus ABI in OpenHuman?

The tinybus ABI is a lightweight RPC interface that enables communication between the OpenHuman core and loadable native modules. It exposes a Proxy struct that marshals calls across the boundary to the compiled cdylib, allowing the core to invoke module-specific APIs defined in contract crates like tinydocs_bus or tinymemory_api without linking the native code at compile time.

How does OpenHuman verify native module integrity?

Integrity verification occurs through two mechanisms: first, the core checks the compiled-in registry in src/openhuman/modules/registry.rs to confirm the module ID is explicitly approved; second, it downloads the platform-specific archive and computes its SHA-256 hash, comparing it against the sha256 field in the PlatformAsset struct. If either check fails, the loading process aborts before dlopen is called.

What is the difference between lazy and eager module loading?

Lazy loading, specified by LoadPolicy::Lazy, defers downloading and initializing the native binary until the user actually requests a feature that requires that module (e.g., tinydocs for document generation). Eager loading, specified by LoadPolicy::Eager, downloads and loads the module during core startup (e.g., tinymemory), ensuring the capability is available immediately for system-critical functions like agent memory management.

How does the wallet module protect recovery phrases?

The wallet module uses attested_proxy, implemented in src/openhuman/modules/wallet.rs at lines 247-254, which performs the SHA-256 digest verification before creating the tinybus proxy. This ensures that the encrypted recovery phrase is only transmitted to a tinywallet binary that matches the exact hash pinned in the registry, preventing exposure to compromised or malicious library versions.

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 →