Configuring DomainSet and ServiceSet for Custom OpenHuman Runtimes: A Complete Guide

Use CoreBuilder with ServiceSet to select transport layers and DomainSet to enable specific domain families, allowing you to tailor the tinyhumansai/openhuman runtime from full desktop applications to headless APIs or embedded libraries.

OpenHuman provides a flexible, two-axis configuration system that lets you customize exactly which services start and which business logic domains are active. By combining ServiceSet and DomainSet through the CoreBuilder API, you can construct runtimes ranging from feature-rich desktop shells to minimal headless workers or pure library embedders.

Understanding the Two-Axis Configuration Model

OpenHuman’s runtime is configured along two independent axes that control orthogonal aspects of the system.

ServiceSet: Transport and Background Services

The ServiceSet axis determines which transport protocols and background services initialize at runtime. This controls how your runtime communicates with the outside world and manages internal scheduling.

Available presets include:

  • ServiceSet::desktop() – Activates all transports and services, including the full HTTP RPC server, Socket.IO, cron schedulers, and channel listeners. This is the configuration used by the desktop Tauri shell.
  • ServiceSet::headless_api() – Starts only the HTTP JSON-RPC server, ideal for cloud deployments and server environments that need API access without UI components.
  • ServiceSet::none() – Disables all transports, creating a pure library mode with no external network services.
  • ServiceSet::embedded() – Configures a long-lived embedder that runs background work and internal processing but exposes no external transport layer.

Pass your chosen preset to CoreBuilder::services(..) when constructing the runtime.

DomainSet: Domain Families and Controllers

The DomainSet axis selects which domain families (controllers, tools, stores, and subscribers) are live at runtime. Each flag corresponds to a DomainGroup that represents a functional area of the system.

Domain families include: agent, memory, threads, config, security, flows, skills, mcp, channels, web3, voice, and media.

Available presets include:

  • DomainSet::full() – Enables every domain family, matching the default behavior of the desktop application.
  • DomainSet::harness() – Activates only core kernel families required for an embeddable agent harness (agent, memory, threads, config, and security).
  • DomainSet::none() – Disables every domain family, leaving only always-on core infrastructure.

Provide the set to CoreBuilder::domains(..) to tailor the feature surface. You can also construct custom sets manually by setting individual boolean flags.

CoreBuilder API and Configuration Methods

The runtime composition happens through CoreBuilder, defined in src/core/runtime/builder.rs. This builder exposes methods to independently set both axes before calling build().await to instantiate the core.

According to the source code in src/embed/harness/builder.rs, the high-level facade exposes these methods for embedders:

use openhuman_core::{
    CoreBuilder, DomainSet, ServiceSet,
};

let core = CoreBuilder::new()
    // Enable only HTTP JSON-RPC and the cron scheduler
    .services(ServiceSet::headless_api())
    // Run only the kernel families required for an embedded agent
    .domains(DomainSet::harness())
    .build()
    .await?;

The chosen sets are stored in the runtime context (src/core/runtime/context.rs), making them available throughout the application lifecycle.

Practical Configuration Examples

Full Desktop Runtime

Use this configuration when building a complete desktop application with all features enabled:

let core = CoreBuilder::new()
    .services(ServiceSet::desktop())   // All transports + services
    .domains(DomainSet::full())        // Every domain family enabled
    .build()
    .await?;

Headless Cloud Worker

Deploy a lightweight server that exposes only HTTP JSON-RPC while maintaining full business logic capabilities:

let core = CoreBuilder::new()
    .services(ServiceSet::headless_api())
    .domains(DomainSet::full())   // Keep all domain families
    .build()
    .await?;

Pure Library and Harness Mode

Create a minimal embeddable runtime with no network exposure and only essential kernel domains:

let core = CoreBuilder::new()
    .services(ServiceSet::none())
    .domains(DomainSet::harness())
    .build()
    .await?;

Custom Domain Selection

Selectively disable specific domains like voice and web3 while keeping other features:

let custom_domains = DomainSet {
    agent: true,
    memory: true,
    threads: true,
    config: true,
    security: true,
    flows: true,
    skills: true,
    mcp: true,
    channels: true,
    web3: false,   // Disabled
    voice: false,  // Disabled
    media: false,
};

let core = CoreBuilder::new()
    .services(ServiceSet::desktop())
    .domains(custom_domains)
    .build()
    .await?;

Embedded Host with Background Work

Run background tasks and internal processing without exposing external RPC endpoints:

let core = CoreBuilder::new()
    .services(ServiceSet::embedded())
    .domains(DomainSet::harness())
    .build()
    .await?;

Key Source Files and Implementation Details

Understanding where these configurations are defined helps when extending or debugging the runtime:

  • src/core/runtime/builder.rs – Contains the struct definitions for ServiceSet and DomainSet, including their preset methods and documentation (lines 32-100).
  • src/embed/harness/builder.rs – Exposes the high-level CoreBuilder API used by embedders and application runners (lines 4-34).
  • src/tui/runner.rs – Demonstrates constructing a core with DomainSet::full() and ServiceSet::none() for the terminal UI runner (lines 122-127).
  • src/core/runtime/context.rs – Holds the runtime context that carries the chosen DomainSet and ServiceSet throughout execution.
  • src/lib.rs – Re-exports CoreBuilder, DomainSet, ServiceSet, and related types for library consumers.

Summary

  • ServiceSet controls transport layers (HTTP RPC, Socket.IO) and background services, with presets for desktop, headless API, embedded, and disabled modes.
  • DomainSet controls which business logic families are active, with presets for full features, minimal harness, or completely disabled.
  • CoreBuilder composes these axes independently via services() and domains() methods before build().await.
  • Configuration definitions live in src/core/runtime/builder.rs, while practical examples appear in src/tui/runner.rs and the harness builder.
  • You can construct manual DomainSet instances with individual boolean flags to create precisely tailored runtimes.

Frequently Asked Questions

What is the difference between ServiceSet and DomainSet in OpenHuman?

ServiceSet controls infrastructure concerns—transports like HTTP JSON-RPC, Socket.IO, and background schedulers—while DomainSet controls application logic by enabling or disabling specific domain families like agent, memory, or web3. They are independent axes, meaning you can run a headless HTTP API with only harness domains, or a desktop app with no transport (as shown in src/tui/runner.rs).

How do I create a headless OpenHuman server?

Pass ServiceSet::headless_api() to CoreBuilder::services() to start only the HTTP JSON-RPC server without Socket.IO or other desktop-specific services. You typically pair this with DomainSet::full() to keep all business logic available via the API, or use DomainSet::harness() for a minimal agent server.

Can I enable specific domains while disabling others in OpenHuman?

Yes. Instead of using presets like DomainSet::full(), instantiate DomainSet directly with struct literal syntax, setting boolean flags for each domain family (agent, memory, threads, config, security, flows, skills, mcp, channels, web3, voice, media). Pass this custom instance to CoreBuilder::domains().

Where are ServiceSet and DomainSet defined in the OpenHuman source code?

Both structs are defined in src/core/runtime/builder.rs (lines 32-100) according to the tinyhumansai/openhuman repository. The CoreBuilder API that consumes these types is exposed through src/embed/harness/builder.rs, and the runtime context holding these values is managed in src/core/runtime/context.rs.

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 →