How TinyMemory Module Extraction Maintains a Consistent Host-Module Contract in OpenHuman

OpenHuman enforces type safety across process boundaries by isolating the TinyMemory wire contract in the standalone tinymemory-api crate, which both the core host and dynamically loaded modules consume as their single source of truth.

The tinyhumansai/openhuman repository solves the TinyMemory module extraction boundary consistency problem by strictly separating the on-the-wire vocabulary from internal implementation details. This architectural pattern ensures that when the TinyMemory module communicates with the host, both sides reference identical type definitions for errors, chunks, and capabilities while allowing the host's internal engine to evolve independently.

Isolating the Contract in tinymemory-api

The foundation of the consistency model lies in the tinymemory-api crate located at vendor/tinymemory/crates/tinymemory-api. This crate serves as a pure, dependency-free interface that defines every type crossing the TinyBus module boundary.

Any data structure that travels between the host and the TinyMemory module—including errors, chunks, capabilities, and RPC payloads—lives exclusively in this crate. By making tinymemory-api a shared dependency, both the host and the independently compiled module are guaranteed to use identical binary layouts for all cross-boundary communication.

Host-Side Contract Consumption via Facade

The host does not directly depend on the vendor path. Instead, src/openhuman/memory/api.rs provides a thin façade that re-exports the entire contract:

pub use tinymemory_api::*;

This pattern allows the core codebase to access the contract through openhuman::memory::api without importing implementation-specific modules. The façade ensures that host code remains agnostic to the physical location of the contract crate while strictly preventing leakage of internal engine types into the public API.

Separating Internal Engine from Wire Types

The host maintains its own in-process engine, referred to as the "direct engine," within tinymemory-core and tinymemory-cortex. These crates are designated as optional dev-dependencies and are explicitly excluded from production binaries.

Internal configuration types such as MemoryHostConfig and EmbeddingProvider deliberately remain outside of tinymemory-api. This separation ensures that host-specific implementation details never accidentally cross the module boundary, preserving the contract's stability even as the internal engine undergoes refactoring.

Compile-Time Contract Verification

To prevent silent type mismatches during development, the repository includes compile-time identity tests in src/openhuman/memory/api_identity_tests.rs. These tests assert that host-facing types are identical to their contract crate counterparts:

#[test]
fn contract_type_ids_match() {
    // If the crate versions diverge, the following type alias will fail to compile
    type _Check = tinymemory_api::MemoryError;
}

If the contract definitions and the host re-export diverge, the compilation fails immediately, enforcing that both sides share the exact same type definitions.

Runtime Compatibility and Version Pinning

When loading the TinyMemory module, the host enforces runtime compatibility through the module loader in src/openhuman/modules/registry.rs. The loader pins a specific SHA-256-verified release of the module and verifies the contract version before initialization.

If the module's compiled contract version differs from the host's expectation, the loader refuses to load the module. This guarantees that only compatible versions execute together, preventing runtime serialization errors that could arise from schema mismatches.

Graceful Degradation with the Null Driver

For builds that exclude the TinyMemory module, the host falls back to a null driver implemented in src/openhuman/memory/binding.rs. The NullMemoryProvider implements the same public traits as the real engine but returns MemoryError::Unsupported for all operations.

Because the contract crate (tinymemory-api) is always compiled into the host, the public API surface remains stable regardless of whether the actual module is present. Only the underlying implementation changes between the null driver and the real engine.

Practical Code Examples

Importing Contract Types in Host Code

Host components import the shared contract through the façade:

use openhuman::memory::api::{MemoryError, Chunk, Capabilities};

fn handle_module_error(err: MemoryError) {
    match err {
        MemoryError::InvalidPath => eprintln!("Invalid path supplied to TinyMemory"),
        MemoryError::Unsupported => eprintln!("Operation not supported by the current module"),
        _ => eprintln!("Other memory error: {:?}", err),
    }
}

Implementing the Contract in a TinyMemory Module

Modules compiled against the same contract use identical types:

use tinymemory_api::{MemoryError, Chunk};

pub fn read_chunk(id: u64) -> Result<Chunk, MemoryError> {
    // Returns the contract's Chunk type guaranteed to match host expectations
    todo!()
}

Conditional Provider Selection

The host switches between the real engine and the null driver based on feature flags:

use openhuman::memory::binding::{MemoryProvider, NullMemoryProvider};

fn build_provider() -> Box<dyn MemoryProvider> {
    if cfg!(feature = "tiny_memory") {
        Box::new(tinymemory_core::MemoryEngine::new())
    } else {
        Box::new(NullMemoryProvider::new())
    }
}

This approach maintains API consistency while allowing the host to compile without the heavy engine dependencies when the module is disabled.

Summary

  • Contract isolation: The tinymemory-api crate at vendor/tinymemory/crates/tinymemory-api defines the definitive wire protocol shared by both host and module.
  • Façade pattern: src/openhuman/memory/api.rs re-exports the contract to host code without exposing implementation internals.
  • Compile-time safety: Identity tests in api_identity_tests.rs enforce type equality between host and contract, failing the build on mismatch.
  • Runtime verification: The module loader in registry.rs pins SHA-256-verified releases and validates contract versions before execution.
  • Implementation independence: Host-specific types like MemoryHostConfig remain in optional dev-dependencies (tinymemory-core), ensuring only contract types cross process boundaries.

Frequently Asked Questions

What is the role of the tinymemory-api crate?

The tinymemory-api crate serves as the single source of truth for all types that cross the TinyBus module boundary. It defines the wire protocol for errors, chunks, capabilities, and RPC payloads in a dependency-free crate that both the host and the TinyMemory module link against, ensuring binary compatibility across process boundaries.

How does OpenHuman prevent type mismatches between the host and TinyMemory module?

OpenHuman employs compile-time identity tests in src/openhuman/memory/api_identity_tests.rs that assert type equality between the host's re-exported types and the original tinymemory-api definitions. Additionally, the runtime loader in src/openhuman/modules/registry.rs verifies SHA-256 digests and contract versions, refusing to load modules with incompatible schemas.

What happens when the TinyMemory module is disabled at compile time?

When the module is disabled, the host compiles a null driver (NullMemoryProvider in src/openhuman/memory/binding.rs) that implements the same traits as the real engine but returns MemoryError::Unsupported for all operations. Because the contract crate is always included, the public API remains unchanged; only the backing implementation switches to the stub.

Why are MemoryHostConfig and EmbeddingProvider excluded from tinymemory-api?

These types represent host-internal implementation details that never traverse the module boundary. By keeping them in separate crates (tinymemory-core, tinymemory-cortex) marked as optional dev-dependencies, the architecture prevents accidental leakage of host-specific configuration into the shared contract, allowing the internal engine to evolve without affecting module compatibility.

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 →