How TinyBus Admits Loadable Native Modules in OpenHuman: The tinydocs and tinyvoice Admission Pipeline
TinyBus admits loadable native modules through a five-stage admission pipeline that includes registry declaration, SHA-256 checksum verification, dlopen-based attachment, and contract-based interface binding, ensuring only authenticated artifacts from the static registry are loaded into the process.
OpenHuman's Rust core extends its capabilities at runtime by loading native cdylib modules such as tinydocs (document generation) and tinyvoice (speech-to-text) through the TinyBus module system. Unlike traditional linked libraries, these modules are discovered, verified, and attached dynamically via a strictly controlled admission pipeline. The TinyBus module system ensures that only cryptographically verified artifacts matching the host's target triple are admitted into the process address space.
The TinyBus Module Admission Pipeline
The admission of loadable modules follows a rigorous five-stage pipeline defined in src/openhuman/modules/registry.rs and src/openhuman/modules/ops.rs. Each stage implements specific security and binding guarantees.
Stage 1: Registry Declaration
TinyBus maintains a static registry table in src/openhuman/modules/registry.rs (lines 10-30) that explicitly lists every module the system may load. Each registry entry contains three critical fields:
- Bus name: The unique identifier (e.g.,
"ai.tinyhumans.tinydocs"or"ai.tinyhumans.tinyvoice") - Object path: The logical path used to locate the module artifact
- SHA-256 digest: The cryptographic hash of the release artifact for integrity verification
This registry acts as a whitelist. If a module is not declared in this static table, TinyBus will refuse to load it regardless of the file's presence on the filesystem.
Stage 2: Artifact Selection and Verification
When the core requests a module (e.g., via the modules.list RPC), TinyBus performs cryptographic verification before loading:
- Asset selection: TinyBus fetches the release's
checksum.tomland selects a matching asset - Tag validation: The system verifies that the URL points to a tagged artifact; branch URLs are explicitly rejected
- Digest validation: The downloaded file's SHA-256 hash must match the digest stored in the registry entry. If the digest differs, TinyBus rejects the download immediately
This verification ensures that only the exact artifact published by Tiny Humans can be loaded, preventing substitution attacks.
Stage 3: Process-Wide Bus Attachment
After passing verification, the module is attached to the process through src/openhuman/modules/ops.rs:
- Broker creation: TinyBus creates a
tinybus::broker::Brokerinstance that will own the module's bus - Dynamic loading: The library is loaded into the process using
dlopen - Address space sharing: The module inherits the same address space, privileges, and crash domain as the core Rust process
Critically, as noted in ops.rs (lines 7-9), TinyBus never unloads a library. Once loaded, the module remains attached for the process lifetime. Any failure—whether refusal, fault, or missing artifacts—is cached permanently, eliminating the "unload-and-reuse-after-exploit" attack surface.
Stage 4: Interface Binding
Each loadable module ships with a companion contract crate (e.g., tinydocs-bus or tinyvoice-bus) located in the vendor/ directory. These contracts define:
- The TinyBus object name
- Method signatures for RPC calls
- Version compatibility information
After loading, the core uses the contract's tinybus::Connection to obtain a proxy object (tinybus::Proxy) that implements the module's RPC interface. For example, calls to GenerateDocument or TranscribeAudio are routed through this proxy, which marshals arguments across the bus boundary while maintaining type safety.
Stage 5: Error Classification
Errors originating from loadable modules are wrapped in tinybus::Error types. The core maps these to domain-specific error types—such as VoiceCallError for tinyvoice or equivalent document errors for tinydocs—so the rest of OpenHuman can handle failures uniformly. The classify function in src/openhuman/modules/voice.rs (lines 491-503) demonstrates this mapping for the voice module.
How tinydocs and tinyvoice Navigate the Pipeline
Both modules follow the identical admission machinery but bind to different bus names and expose distinct capabilities.
tinydocs Module Flow
- Bus name:
"ai.tinyhumans.tinydocs" - Contract crate:
tinydocs-buslocated invendor/tinydocs - Key method:
GenerateDocx - Admission path: When the core needs to generate a document, it calls
modules::documents::attested_proxy, which coordinates the registry lookup, verification, anddlopensequence, returning atinybus::Proxyready to invokeGenerateDocx
tinyvoice Module Flow
- Bus name:
"ai.tinyhumans.tinyvoice" - Contract crate:
tinyvoice-bus - Key methods:
TranscribeAudioandGenerateSpeech - Admission path: The core obtains a proxy via
modules::voice::attested_proxy. The proxy creation logic insrc/openhuman/modules/voice.rs(lines 491-505) handles the broker connection and proxy instantiation
Because TinyBus creates the broker once per process, subsequent calls to either module reuse the same in-process bus, eliminating repeated load overhead while maintaining isolation boundaries.
Security Guarantees of the TinyBus Module System
The admission pipeline implements three critical security controls:
Digest-Based Authenticity
The SHA-256 digest stored in src/openhuman/modules/registry.rs guarantees that only the exact artifact published by Tiny Humans can be loaded. Any modification to the binary—whether malicious or accidental—results in a digest mismatch and immediate rejection.
Target-Triple Validation
Before loading, src/openhuman/modules/platform.rs validates that the binary matches the host's target triple (architecture and platform). This prevents accidental loading of incompatible builds (e.g., macOS binaries on Linux hosts) that could cause undefined behavior.
Never-Unload Policy
Once a module passes verification and attaches via dlopen, it remains mapped for the process lifetime. This design eliminates the classic attack surface where an exploit might trigger an unload, modify the library on disk, and force a reload of compromised code.
Implementing Module Calls in Practice
The following Rust example demonstrates loading the tinydocs module and invoking document generation:
use openhuman::modules::{self, Config};
use tinybus::Proxy;
async fn generate_document(input_markdown: &str, output_path: &str) -> Result<(), modules::Error> {
// Load configuration containing the modules section
let config = Config::load().await?;
// Obtain an attested proxy for the documents module
// This performs registry lookup, checksum verification, and dlopen if needed
let docx_proxy: Proxy = modules::documents::attested_proxy(&config).await?;
// Invoke the method defined in the tinydocs-bus contract
let result = docx_proxy
.call("GenerateDocx", (input_markdown, output_path))
.await?;
// Convert TinyBus errors into OpenHuman-specific error types
tinybus::Result::from(result)
.map_err(modules::documents::classify)
}
To use tinyvoice instead, substitute modules::documents with modules::voice and call methods like TranscribeAudio using the same proxy pattern.
Summary
- TinyBus uses a static registry in
src/openhuman/modules/registry.rsto whitelist allowable modules and their SHA-256 digests - Cryptographic verification against
checksum.tomland target-triple checks inplatform.rsensure artifact integrity beforedlopen - Contract crates (
tinydocs-bus,tinyvoice-bus) define type-safe RPC interfaces viatinybus::Proxyobjects - The never-unload policy prevents runtime exploitation by caching failures and prohibiting library unmapping
- Modules like tinydocs and tinyvoice share identical admission machinery but bind to unique bus names (
ai.tinyhumans.tinydocs,ai.tinyhumans.tinyvoice)
Frequently Asked Questions
How does TinyBus verify module authenticity before loading?
TinyBus verifies authenticity through a two-step process defined in src/openhuman/modules/registry.rs. First, it checks that the module exists in the static registry table. Second, it downloads the artifact from the tagged release URL and validates that its SHA-256 digest matches the hash stored in the registry. If either check fails, the module is rejected before dlopen is ever called.
Can loadable native modules be unloaded dynamically in OpenHuman?
No. According to the source code in src/openhuman/modules/ops.rs, TinyBus implements a never-unload policy. Once a module is loaded via dlopen, it remains attached to the process for its entire lifetime. This design decision eliminates security vulnerabilities related to runtime library substitution and ensures consistent behavior after initial load.
What happens if a module's SHA-256 checksum doesn't match the registry entry?
If the downloaded artifact's SHA-256 digest differs from the value stored in src/openhuman/modules/registry.rs, TinyBus immediately rejects the download and refuses to load the module. The error is cached for the process lifetime, preventing retry loops. The system will not fall back to untrusted sources or proceed with an unverified binary.
How do I add a new loadable module to the TinyBus system?
Adding a module requires three steps: First, create the native cdylib library and a corresponding contract crate defining the TinyBus interface (object name and methods). Second, add an entry to the static registry in src/openhuman/modules/registry.rs containing the bus name, object path, and expected SHA-256 digest. Third, implement an attested_proxy function in a new file under src/openhuman/modules/ (following the pattern in voice.rs or documents.rs) that handles the proxy creation and error classification for your specific domain.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →