OpenHuman Memory Engine Architecture: Understanding tinymemory-api, tinymemory, tinymemory-core, and tinycortex

The OpenHuman memory engine comprises four layered Rust crates—tinymemory-api for the wire contract, tinymemory for the host façade, tinymemory-core for the storage engine, and tinycortex for graph-centric operations—that together provide a type-safe, persistent knowledge store.

The OpenHuman repository implements a contract-first memory subsystem designed for extensibility and runtime flexibility. At the heart of this system are four specialized crates that separate interface definitions from implementation details. This architecture enables the core application to swap storage backends while maintaining type-level compatibility across native and dynamic module boundaries.

The Four Core Components

The memory stack organizes functionality into distinct layers, each with specific responsibilities and locations within the codebase.

tinymemory-api: The Wire Contract

tinymemory-api serves as the canonical source of truth for all types, errors, and RPC surfaces exchanged between the core process and memory modules. Located in src/openhuman/memory/api.rs and vendored at vendor/tinymemory/, this crate ensures that no other component redefines shared structures.

The contract defines fundamental types including Chunk, Capabilities, MemoryError, and Provider. It enforces version negotiation through the CONTRACT_VERSION constant, allowing the runtime to detect incompatible modules before executing operations. The core crate uses pub use tinymemory_api::* to import these definitions, guaranteeing that the host and any loaded modules speak the same language at the type level.

tinymemory: The Host-Side Façade

tinymemory acts as the host-side implementation that exposes the contract to the rest of the application. The implementation resides in src/openhuman/memory/host_impls.rs and the vendored submodule vendor/tinymemory, where it instantiates the concrete engine and handles host-specific concerns like configuration parsing and key-ring access.

This crate provides the MemoryProvider trait implementation that the OpenHuman runtime calls. When the system initializes, src/openhuman/memory/host.rs constructs the provider using MemoryEngine::new, bridging the high-level contract to the low-level core. The façade also supplies a default "null" provider when no memory module is loaded, ensuring the application starts gracefully even without persistent storage.

tinymemory-core: The Storage Engine

tinymemory-core contains the actual engine responsible for persisting, indexing, and serving chunks, goals, documents, and other artifacts. Deliberately dependency-free except for serde and tokio, this crate lives in vendor/tinymemory/crates/tinymemory-core and can be compiled into both the core binary and a dynamic module (tinymemory-module).

The engine implements ChunkStore for persistence and MemoryGraph for queries. It exposes low-level APIs including read_chunk, write_chunk, and list_chunks that the contract layers invoke. Direct usage appears in the test file src/openhuman/memory/direct_engine_refs_tests.rs, which demonstrates instantiating MemoryEngine and performing raw chunk operations without the RPC layer.

tinycortex: The Graph-Centric Wrapper

tinycortex provides a high-level, hierarchical view of memory as a directed acyclic graph. Shipped as a separate vendored crate at vendor/tinycortex, it builds on top of tinymemory-core to offer semantic enrichment and traversal capabilities. The goals domain in src/openhuman/memory/goals/mod.rs imports this crate to implement goal-oriented memory features.

This component represents stored knowledge as a DAG and supplies utilities like add_goal and enrich_goal for building hierarchical structures. It performs fast similarity searches and graph traversals that sit atop the raw chunk storage, allowing higher-level domains to reason about relationships rather than individual bytes.

How the Components Interact

The architecture follows a strict layering principle that guarantees type-level compatibility and runtime flexibility.

  1. Contract Layer: All RPC calls from the UI or external agents travel through JSON-RPC payloads defined by tinymemory-api, such as the ReadChunkInput struct.

  2. Host Façade: src/openhuman/memory/host.rs and host_impls.rs expose a MemoryProvider implementation that satisfies the contract. This provider validates incoming requests against the CONTRACT_VERSION before forwarding them.

  3. Engine Execution: The provider delegates storage operations to tinymemory-core, which manages on-disk data structures. Because the core is pure Rust, the system can also load it as a dynamic tinymemory-module through the plugin system.

  4. Graph Operations: Higher-level domains import tinycortex to obtain a graph view of stored chunks. These domains call back into the provider for raw chunk operations while maintaining their own semantic indices.

Practical Implementation Examples

Creating a Memory Provider at Startup

To initialize the memory subsystem, the application loads configuration and constructs the provider through the host façade:

use openhuman::memory::{host::MemoryHost, MemoryProvider};
use openhuman::memory::api::ProviderConfig;

// Load configuration from the OpenHuman TOML file
let cfg = ProviderConfig::load_or_init_with_env_lookup()?;

// Build the in-process provider – this uses the tinymemory façade
let provider: Box<dyn MemoryProvider> = MemoryHost::new(cfg)?.into_provider();

Source: src/openhuman/memory/host.rs

Writing a Chunk via the Contract API

The following example demonstrates creating and storing a chunk through the contract layer:

use openhuman::memory::api::{WriteChunkInput, Chunk};
use openhuman::memory::ops::provider::write_chunk;

// Create a chunk (the raw data the engine stores)
let chunk = Chunk {
    id: "doc-123".into(),
    content: b"Hello, world!".to_vec(),
    metadata: Default::default(),
    ..Default::default()
};

let input = WriteChunkInput {
    chunk,
    ..Default::default()
};

let result = write_chunk(&provider, input).await?;
println!("Stored chunk with version {}", result.version);

Source: src/openhuman/memory/ops/provider.rs

Direct Engine Access for Low-Level Operations

For scenarios requiring bypass of the RPC layer, you can interact directly with tinymemory-core:

use tinymemory_core::engine::MemoryEngine;
use tinymemory_core::chunk::ChunkId;

// Assume we have a `MemoryEngine` already constructed (e.g. from tests)
let engine = MemoryEngine::open("/tmp/openhuman_memory")?;
let id = ChunkId::new("doc-123");

// Direct engine call – bypasses the contract layer
let chunk = engine.read_chunk(&id).await?;
println!("Chunk payload: {}", String::from_utf8_lossy(&chunk.content));

Source: src/openhuman/memory/direct_engine_refs_tests.rs

Adding Goals with TinyCortex

Working with hierarchical goals requires the graph-oriented API provided by TinyCortex:

use tinycortex::graph::Graph;
use openhuman::memory::goals::ops::enrich::enrich_goal;

// Build a TinyCortex graph backed by the same storage directory
let graph = Graph::open("/tmp/openhuman_memory")?;

// Create a new goal node
let goal_id = graph.add_node("plan-trip", None)?;

// Run the enrichment tool which may add related documents, locations, etc.
enrich_goal(&graph, goal_id).await?;

Source: src/openhuman/memory/goals/enrich.rs

Summary

  • tinymemory-api establishes the contract layer in src/openhuman/memory/api.rs, defining shared types like Chunk and MemoryError to prevent drift between host and module implementations.
  • tinymemory provides the host-side façade through src/openhuman/memory/host_impls.rs, connecting the contract to the concrete engine while handling configuration and lifecycle management.
  • tinymemory-core delivers the dependency-free storage engine at vendor/tinymemory/crates/tinymemory-core, implementing raw persistence and retrieval operations via ChunkStore and MemoryGraph.
  • tinycortex offers graph-centric abstractions over the core engine, enabling hierarchical goal management and semantic traversals in higher-level domains like src/openhuman/memory/goals/.

Frequently Asked Questions

What is the purpose of separating tinymemory-api from the implementation crates?

The separation ensures type-level compatibility across the entire memory stack. By defining all structs, errors, and RPC signatures in tinymemory-api, both the host process and any dynamically loaded modules import identical definitions from src/openhuman/memory/api.rs. This prevents versioning conflicts that would occur if the host and module defined Chunk or MemoryError independently.

Can tinymemory-core run independently of the OpenHuman host process?

Yes, tinymemory-core is deliberately designed with minimal dependencies—only serde and tokio—allowing it to compile as a standalone dynamic module (tinymemory-module) or run embedded in the host. The test file src/openhuman/memory/direct_engine_refs_tests.rs demonstrates direct instantiation of MemoryEngine without the surrounding OpenHuman runtime, proving the core crate operates independently.

How does tinycortex differ from the raw storage provided by tinymemory-core?

While tinymemory-core handles byte-level persistence through read_chunk and write_chunk, tinycortex adds a semantic layer that treats memory as a directed acyclic graph. Located in vendor/tinycortex, it provides goal-oriented APIs like add_goal and enrich_goal that src/openhuman/memory/goals/mod.rs uses to build hierarchical knowledge structures atop the raw chunk storage.

Where is the version compatibility check implemented between the host and memory modules?

The CONTRACT_VERSION constant defined in tinymemory-api enables runtime version negotiation. When src/openhuman/memory/host.rs initializes a provider via MemoryHost::new, it validates that any loaded module adheres to the contract version exposed through src/openhuman/memory/api.rs, ensuring API compatibility before accepting RPC calls.

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 →