How to Embed OpenHuman as a Rust Library Using CoreBuilder and Harness APIs
OpenHuman provides a pure Rust core that can be embedded in any Rust application by configuring a CoreBuilder to assemble a CoreRuntime, then wrapping it with the Harness API to execute AI turns without managing JSON-RPC plumbing or authentication tokens.
The tinyhumansai/openhuman repository ships a modular AI agent framework designed for library embedding. When embedding OpenHuman as a Rust library using CoreBuilder and Harness APIs, you programmatically control model providers, workspace directories, and tool visibility while the Harness abstraction handles session persistence, sandboxing, and result parsing.
Configuring the CoreBuilder
The embedding process starts with CoreBuilder, defined in src/core/runtime/builder.rs. This type implements a fluent builder pattern that configures the runtime environment before instantiation.
Selecting HostKind and Authentication
Every CoreBuilder requires a HostKind that describes the embedding environment. For pure library embedders, use HostKind::Cli to indicate a headless, non-GUI context.
use openhuman_core::core::runtime::{CoreBuilder, HostKind, TokenSource};
let builder = CoreBuilder::new(HostKind::Cli)
.token(TokenSource::Fixed("secure-token".into()));
The TokenSource parameter seeds the per-process RPC bearer token. Options include TokenSource::Fixed for explicit strings, environment file lookups, or automatically generated tokens.
Defining Services and Domains
Control background behavior with ServiceSet and DomainSet:
ServiceSet::none()– Disables background transports (HTTP RPC, socket.io, cron). Use this for headless automation.ServiceSet::desktop()– Enables full desktop services suitable for Tauri or Electron-style shells.
DomainSet determines which domain families are active:
DomainSet::harness()– Loads only essential families (agent, memory, threads, config, security).DomainSet::full()– Activates all available domain controllers.
use openhuman_core::core::runtime::{ServiceSet, DomainSet};
let builder = CoreBuilder::new(HostKind::Cli)
.services(ServiceSet::none())
.domains(DomainSet::harness());
Workspace and Tool Visibility
Configure persistent storage and tool exposure through dedicated methods:
let builder = CoreBuilder::new(HostKind::Cli)
.workspace("./my_workspace")
.action_dir("./actions")
.backend_url("https://api.example.com")
.tool_groups(
ToolGroups::none()
.with("documents", GroupMode::Advertised)
);
The ToolGroups API (defined in src/openhuman/tools/toolpacks/groups.rs) controls which tool collections appear on the wire using modes like GroupMode::Advertised or GroupMode::Withheld.
Building the CoreRuntime
Call .build().await? to consume the CoreBuilder and produce a CoreRuntime:
let core_runtime = CoreBuilder::new(HostKind::Cli)
.services(ServiceSet::none())
.domains(DomainSet::harness())
.token(TokenSource::Fixed("dummy-token".into()))
.workspace("./my_workspace")
.build()
.await?;
This step, implemented in src/core/runtime/builder.rs, registers all controllers, loads the master encryption key, seeds the RPC bearer token, and prepares internal stores. No network listeners start during this phase, making it safe to embed in restricted environments.
Controlling Execution with the Harness API
For most applications, the low-level CoreRuntime should be wrapped with Harness, defined in src/embed/harness/mod.rs. This high-level API manages the RPC server lifecycle and provides typed methods to drive AI turns.
Configuring Model Providers and Access
Use Harness::builder() to fluently configure the execution context:
use openhuman_core::embed::harness::{Harness, Access};
let harness = Harness::builder()
.core_runtime(core_runtime)
.provider(openhuman_core::providers::openai_compatible(
"https://api.openai.com/v1".to_string(),
"sk-...".to_string(),
))
.model("gpt-4o-mini")
.access(Access::full())
.build()
.await?;
The Access tier controls sandboxing permissions. Access::full() grants read/write, network, and installation rights, while restricted tiers limit filesystem and network access.
Running AI Turns
Execute single-turn prompts or multi-turn sessions:
harness.run(prompt).await?– Runs a single turn and returns the output.harness.turn(prompt).session(&session_id).send().await?– Continues an existing conversation thread.
let reply = harness.run("Summarize the OpenHuman architecture.").await?;
println!("AI reply: {}", reply.output_text);
The Harness handles model interaction, tool execution, approval-gate handling, and JSON parsing internally.
Complete Implementation Examples
Minimal Headless Embedder
The file examples/embed_headless.rs demonstrates a pure-library usage with no background services:
use openhuman_core::{
core::runtime::{CoreBuilder, DomainSet, ServiceSet, TokenSource},
embed::harness::{Harness, Access},
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let core = CoreBuilder::new(openhuman_core::HostKind::Cli)
.services(ServiceSet::none())
.domains(DomainSet::harness())
.token(TokenSource::Fixed("dummy-token".into()))
.workspace("./my_workspace")
.build()
.await?;
let harness = Harness::builder()
.core_runtime(core)
.provider(openhuman_core::providers::openai_compatible(
"https://api.openai.com/v1".to_string(),
"sk-...".to_string(),
))
.access(Access::full())
.build()
.await?;
let reply = harness.run("Summarize the OpenHuman architecture.").await?;
println!("AI reply: {}", reply.output_text);
Ok(())
}
Desktop-Style Integration
For applications requiring full desktop services (like the Tauri app), use ServiceSet::desktop() and DomainSet::full() as shown in examples/embed_kernel.rs:
use openhuman_core::{
core::runtime::{CoreBuilder, HostKind, ServiceSet, DomainSet},
embed::harness::{Harness, Access},
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let core = CoreBuilder::new(HostKind::Cli)
.services(ServiceSet::desktop())
.domains(DomainSet::full())
.build()
.await?;
let harness = Harness::builder()
.core_runtime(core)
.access(Access::full())
.build()
.await?;
let output = harness.run("Explain how the tool-calling system works.").await?;
println!("{}", output.output_text);
Ok(())
}
Summary
CoreBuilderinsrc/core/runtime/builder.rsconfigures the embedding environment throughHostKind,ServiceSet, andDomainSetbefore producing aCoreRuntime.CoreRuntimerepresents the initialized core with registered controllers and stores, but does not automatically start network listeners.Harnessinsrc/embed/harness/mod.rswraps the runtime to provide high-level methods likerun()andturn()while managing RPC tokens and sandboxing.- Headless embedders should use
ServiceSet::none()andDomainSet::harness()to minimize resource usage. - Desktop shells should use
ServiceSet::desktop()andDomainSet::full()to enable background transports and full domain support.
Frequently Asked Questions
What is the difference between CoreBuilder and Harness?
CoreBuilder is the low-level configuration API that constructs a CoreRuntime, handling workspace setup, token generation, and domain registration. Harness is a high-level wrapper that consumes a CoreRuntime and provides ergonomic methods like run() to execute AI turns without dealing with JSON-RPC plumbing, session management, or tool approval flows.
Can I embed OpenHuman without starting background network services?
Yes. Pass ServiceSet::none() to CoreBuilder::services() during configuration. This disables HTTP RPC, socket.io, and cron transports, creating a headless embedder suitable for CLI tools or serverless functions. The examples/embed_headless.rs file demonstrates this pattern.
How do I configure which tools are available to the embedded agent?
Use the ToolGroups API via CoreBuilder::tool_groups(). You can start with ToolGroups::none() and selectively expose specific groups using .with("group_name", GroupMode::Advertised). This controls visibility as defined in src/openhuman/tools/toolpacks/groups.rs, allowing you to restrict filesystem, network, or custom tool access based on the embedding context.
What authentication token does the embedded core require?
The core requires an RPC bearer token for internal communication security, supplied via TokenSource. For embedded scenarios, use TokenSource::Fixed("your-token") to provide a static string, or allow the builder to generate one automatically. This token secures the in-process RPC channel between the Harness and the CoreRuntime.
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 →