# How Loadable TinyBus Native Modules Extend OpenHuman Without Recompilation

> Discover how loadable TinyBus native modules extend OpenHuman without recompilation. Learn about Rust ABI contracts, manifest registration, and dynamic loading via dlopen for flexible expansion.

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

---

**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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/registry.rs). When the boot loader in [`src/openhuman/modules/boot.rs`](https://github.com/tinyhumansai/openhuman/blob/main/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.