TinyCortex Memory: How the In-Process Engine Powers OpenHuman Memory Trees

TinyCortex Memory is the core in-process engine inside the OpenHuman Rust binary that manages hierarchical memory trees using lock-free data structures, providing O(1) snapshot reads and atomic persistence to SQLite without IPC overhead.

TinyCortex Memory serves as the central nervous system for the OpenHuman project, handling all persistent data as hierarchical trees within the same process as the core runtime. Implemented in the openhuman-core crate, this engine eliminates cross-process communication delays by keeping the memory tree in the application's address space. It powers everything from conversation history to document storage using a directed acyclic graph model that supports fast ancestor queries and deterministic versioning.

Architectural Overview

The Memory Tree Data Model

At its heart, TinyCortex Memory organizes data as a memory tree—a directed acyclic graph where each node represents a discrete piece of content such as a message, document, or tool result. Every node possesses a unique path_scope (e.g., /chat/2024-08-28) and optional metadata including tags and timestamps. This structure enables efficient traversal of deep conversation histories while maintaining referential integrity across parent-child relationships.

The tree structure is defined in the core memory modules, with the domain entry point at src/openhuman/memory/mod.rs. This file re-exports the public API and registers the memory domain controllers when DomainSet::memory is enabled in the runtime configuration.

In-Process Execution Model

Unlike external memory services that require network calls or IPC, TinyCortex Memory runs in-process inside the OpenHuman core binary. It uses Rust's Arc (atomic reference counting) and RwLock primitives to manage concurrency: read-only snapshots are shared across concurrent turns through Arc clones, while writes are serialized through a single "write gate" to prevent conflicts.

This design choice eliminates the ~10ms overhead typical of RPC round-trips, allowing the LLM to read entire subtrees or write new nodes with minimal latency. The engine maintains the entire tree lifecycle—including creation, persistence, pruning, and snapshotting—within the same address space, as documented in the migration plans.

Persistence and Versioning Layer

Durability is handled through a dual-layer approach. Hot data resides in RAM within MemoryNode structures, while the SQLite database (memory.db) provides atomic transactional storage. A background service (memory::git::git_sync_worker in src/openhuman/memory/git.rs) mirrors state changes into a git-backed ledger, writing MemoryDiff objects that create an immutable audit trail.

This architecture supports deterministic replay and rollback capabilities, as each snapshot can be reconstructed from the sequence of diffs stored in the git repository.

How TinyCortex Memory Works Internally

Tree Initialization and Root Creation

When the OpenHuman core starts, the function memory::init::init_memory_tree() constructs the root node representing the tree base (/). This initialization assigns a unique ID and timestamp to the root MemoryNode, establishing the foundation for all subsequent operations.

The initialization logic resides within the memory domain registration flow in src/core/all.rs, ensuring that memory RPC endpoints (/rpc/memory/*) are available only when the domain is explicitly enabled.

Node Insertion via Path Scopes

New content enters the tree through memory::ops::insert_chunk() (defined in src/openhuman/memory/ops.rs). This operation creates a Chunk object containing the payload and metadata, then attaches it to a parent node identified by its path_scope.

The insertion process updates both the in-memory MemoryNode structures and the SQLite chunks table atomically. By using path-based scoping (e.g., /chat/2024-08-28/message-001), the engine maintains logical groupings that mirror the application's domain structure.

Traversal and In-Memory Caching

Queries such as memory::ops::query_subtree() utilize depth-first iterators that traverse the children vectors stored in each MemoryNode. Because the engine caches entire subtrees in RAM, these operations yield Chunk objects without additional database hits once the data is loaded.

This caching strategy enables O(1) access to existing nodes through shared references, dramatically reducing latency when the LLM needs to retrieve conversation context or historical tool outputs during active turns.

Versioned Snapshots for Consistent Views

At the end of each processing turn, the engine calls memory::ops::snapshot() to clone the Arc<MemoryNode> representing the current tree root. This creates an immutable, versioned view that can be passed to asynchronous tools or subsequent turns.

Because snapshots leverage atomic reference counting, tools can hold consistent views of the tree state even as the main thread continues inserting new nodes. The snapshotting mechanism guarantees that parallel "what-if" reasoning branches do not interfere with the canonical tree state.

Working with TinyCortex Memory: Code Examples

The tinymemory-api contract exposes these capabilities through the openhuman::memory module. Below are practical examples demonstrating insertion, querying, and snapshotting:

// Insert a new memory chunk (e.g., a user message)
use openhuman::memory::{ops, Chunk, Metadata};

let msg = Chunk::new("User says: Hello!".into());
let meta = Metadata::default().with_tag("chat");
ops::insert_chunk(msg, "/chat/2024-08-28", meta)?;
// Query a subtree to retrieve all chat messages under a specific date
use openhuman::memory::ops::query_subtree;

let subtree = query_subtree("/chat/2024-08-28")?;
for chunk in subtree {
    println!("Message: {}", String::from_utf8_lossy(&chunk.payload));
}
// Take a snapshot for a tool that runs asynchronously
use openhuman::memory::ops::snapshot;
use std::sync::Arc;

let snap: Arc<MemoryNode> = snapshot()?;
tool::run_async(move || {
    // The tool sees a stable view even if new nodes are inserted later
    let view = snap.clone();
    // Process the snapshot...
});

All public types including Chunk, Metadata, and MemoryNode are defined in the tinymemory-api contract and re-exported through src/openhuman/memory/api.rs.

Summary

  • TinyCortex Memory is an in-process engine within OpenHuman's Rust core that manages hierarchical memory trees without IPC overhead.
  • The engine uses path scopes and MemoryNode structures to create a directed acyclic graph optimized for ancestor/descendant queries.
  • Concurrency is handled via Arc and RwLock, allowing O(1) snapshot reads and serialized writes through a single write gate.
  • Persistence combines SQLite atomic transactions with a git-backed diff ledger for auditability and rollback support.
  • Core operations including insert_chunk(), query_subtree(), and snapshot() are implemented in src/openhuman/memory/ops.rs and exposed through the tinymemory-api contract.

Frequently Asked Questions

What is the difference between TinyCortex Memory and a traditional database?

TinyCortex Memory operates as an in-process engine rather than a separate database service. While it uses SQLite for persistence, the active tree lives entirely in RAM within the OpenHuman process, eliminating network latency and serialization costs. Traditional databases require IPC or network calls, whereas TinyCortex Memory shares memory directly with the LLM runtime through Arc<MemoryNode> references, enabling microsecond-scale access times for cached data.

How does TinyCortex Memory handle concurrent read and write operations?

The engine implements a read-heavy concurrency model using Rust's Arc (atomic reference counting) for shared ownership and RwLock for synchronization. Multiple readers can hold snapshots simultaneously without blocking, while writes are serialized through a single "write gate" to ensure consistency. This design allows the LLM to read historical context in O(1) time while new chunks are being inserted, with locks held only for milliseconds during atomic commit operations.

Where is the memory tree data physically stored?

Data persists in two locations: a local SQLite database (memory.db) stores the current state of all chunks and metadata, while a git-backed ledger maintains an immutable history of changes. The background worker defined in src/openhuman/memory/git.rs periodically writes MemoryDiff objects to the git repository, creating a tamper-evident audit trail. This dual-layer approach combines the speed of in-memory access with the durability and versioning capabilities of transactional storage.

Can TinyCortex Memory be accessed from external processes or only within OpenHuman?

Currently, TinyCortex Memory is strictly in-process and designed to run exclusively within the OpenHuman core binary. All access occurs through the tinymemory-api contract exposed via src/openhuman/memory/api.rs. While the internal RPC endpoints (/rpc/memory/*) provide structured access for native modules loaded by the core, there is no external IPC interface—this design prioritizes security and latency by keeping memory operations within the same sandbox and address space as the LLM runtime.

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 →