How Loadable TinyBus Native Modules Extend OpenHuman Without Recompilation

Loadable TinyBus native modules extend OpenHuman's core without recompilation by implementing a standardized Rust ABI contract, registering in a compile-time manifest that maps artifacts to SHA-256 hashes, and loading dynamically via dlopen during the boot sequence.

OpenHuman is a Rust-based AI runtime that executes inside a Tauri desktop shell, separating built-in domains like memory and agents from external capabilities. The TinyBus plugin system allows developers to distribute new functionality as native binaries (.so, .dylib, or .dll) that the core downloads, cryptographically verifies, and hot-loads at startup without modifying the core's codebase or triggering a rebuild.

The TinyBus ABI Contract

Every loadable module must implement the TinyBus ABI, a minimal set of exported symbols defined in the tinymodule-bus crate located in vendor/tinymodule-bus. This contract exposes the module's CONTRACT_VERSION, a unique bus identifier, and RPC-style method signatures that the core uses to generate client bindings. By adhering to this interface, modules compiled independently in separate repositories can link dynamically against the running OpenHuman process.

The ABI version check occurs immediately after dlopen; if the module's reported version mismatches the core's expected contract, the loader aborts initialization to prevent undefined behavior.

The Module Registry Architecture

OpenHuman maintains a compile-time registry in src/openhuman/modules/registry.rs that declares every approved module release. The registry consists of a constant slice of ModuleInfo structs, each specifying the module name, semantic version, supported target triple (e.g., x86_64-unknown-linux-gnu), download URL, and SHA-256 digest.

Because this registry is a const array embedded in the binary, the core knows how to locate and verify modules without hardcoded file paths. Adding a new capability requires only appending a ModuleInfo entry to the registry and publishing the compiled artifact; the existing Rust core requires no recompilation to recognize the new module at runtime.

Runtime Loading in the Boot Sequence

The module loader in src/openhuman/modules/boot.rs orchestrates dynamic extension during the core's startup phase. The sequence follows four strict steps:

  1. Artifact Selection: Query the registry for a ModuleInfo matching the host architecture.
  2. Cache Management: Download the binary to a local cache path if absent, using the URL from the registry.
  3. Integrity Verification: Compare the file's SHA-256 hash against the digest stored in registry.rs; fail closed on mismatch.
  4. Dynamic Linking: Invoke dlopen (via libloading) to load the library, then initialize a OnceBus::init_in_process broker to register the module's services on the TinyBus.

If any step fails, the loader logs the error and continues, ensuring the core remains operational even with missing or corrupted modules.

Domain Integration via Generated Clients

Core domains consume modules through auto-generated TinyBus clients rather than direct FFI calls. For example, the documents domain in src/openhuman/modules/documents.rs imports tinydocs_bus::Client to delegate PDF generation to the native tinydocs module.

The client marshals requests onto the TinyBus RPC layer, where the broker routes them to the loaded module's implementation. Because domains interact only with the generated client API, modifications to the module's internal logic or a complete binary swap do not necessitate recompiling the consuming domain code.

Security and Versioning Guarantees

OpenHuman enforces three layers of trust for loadable code:

  • Cryptographic Verification: The SHA-256 hash in the ModuleInfo struct ensures only the exact artifact approved by the registry author can execute.
  • ABI Compatibility: The CONTRACT_VERSION constant prevents loading modules built against incompatible interface definitions.
  • Sandbox Policy: Modules execute within the same address space but inherit the core's sandbox restrictions, including filesystem and network access controls defined in the Tauri capabilities configuration.

Summary

  • TinyBus ABI: A standardized contract in vendor/tinymodule-bus that defines how modules expose functionality to the core.
  • Registry Pattern: A const array in src/openhuman/modules/registry.rs mapping module metadata and cryptographic hashes to downloadable artifacts.
  • Dynamic Loading: The boot sequence in src/openhuman/modules/boot.rs handles dlopen, SHA-256 verification, and bus registration without core recompilation.
  • Client Generation: Domains use generated clients (e.g., tinydocs_bus::Client) to call modules over TinyBus, decoupling domain logic from module implementations.
  • Security Model: Runtime integrity checks via SHA-256 and version constants ensure only authorized, compatible modules load.

Frequently Asked Questions

What is the TinyBus ABI and why is it required?

The TinyBus ABI is a Rust-based interface contract defined in the tinymodule-bus crate that specifies how native modules export their version identifier, bus name, and RPC methods. It is required because it provides the type-safe boundary between the statically compiled OpenHuman core and dynamically loaded native code, ensuring memory safety and API compatibility across independent compilation units.

How does OpenHuman verify module integrity without recompiling the core?

OpenHuman verifies integrity using SHA-256 hashes stored in the compile-time registry at src/openhuman/modules/registry.rs. When the boot loader in src/openhuman/modules/boot.rs downloads a module, it calculates the file's digest and compares it against the hardcoded hash in the ModuleInfo struct, rejecting the binary if the values diverge.

Can modules be updated independently of the core application?

Yes, modules update independently by publishing new artifact versions to the URLs specified in the registry. Because the registry contains version-specific metadata and cryptographic fingerprints, the core can detect outdated cached binaries, download the latest release, and load it on the next startup without requiring a new OpenHuman build.

What happens if a module fails to load during the boot sequence?

If a module fails to load—whether due to network errors, hash mismatches, or ABI version conflicts—the boot loader records the specific failure in the log and continues initialization. The core operates gracefully in a degraded state, providing built-in domain functionality while excluding the malfunctioning module's capabilities from the TinyBus service registry.

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 →