# How to Embed OpenHuman as a Rust Library Using CoreBuilder and Harness APIs

> Embed OpenHuman in Rust apps using CoreBuilder and Harness APIs. Build a CoreRuntime and execute AI turns seamlessly, bypassing JSON-RPC and authentication management.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-09-01

---

**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`](https://github.com/tinyhumansai/openhuman/blob/main/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.

```rust
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.

```rust
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:

```rust
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`](https://github.com/tinyhumansai/openhuman/blob/main/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`**:

```rust
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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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:

```rust
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.

```rust
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`](https://github.com/tinyhumansai/openhuman/blob/main/examples/embed_headless.rs) demonstrates a pure-library usage with no background services:

```rust
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`](https://github.com/tinyhumansai/openhuman/blob/main/examples/embed_kernel.rs):

```rust
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

- **`CoreBuilder`** in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) configures the embedding environment through `HostKind`, `ServiceSet`, and `DomainSet` before producing a `CoreRuntime`.
- **`CoreRuntime`** represents the initialized core with registered controllers and stores, but does not automatically start network listeners.
- **`Harness`** in [`src/embed/harness/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/embed/harness/mod.rs) wraps the runtime to provide high-level methods like `run()` and `turn()` while managing RPC tokens and sandboxing.
- **Headless embedders** should use `ServiceSet::none()` and `DomainSet::harness()` to minimize resource usage.
- **Desktop shells** should use `ServiceSet::desktop()` and `DomainSet::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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`.