# How ServiceSet, DomainSet, and ToolGroups Are Configured at Runtime in OpenHuman

> Discover how OpenHuman configures runtime behavior using ServiceSet, DomainSet, and ToolGroups. Learn how these builder axes are set via the CoreBuilder API before instantiation.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: internals
- Published: 2026-08-24

---

**OpenHuman configures runtime behavior through three orthogonal builder axes—ServiceSet for transport selection, DomainSet for domain family activation, and ToolGroups for tool visibility—each set via the CoreBuilder API before instantiation.**

The OpenHuman core runtime uses a declarative builder pattern to assemble background services, domain controllers, and agent tool registries. Understanding how **ServiceSet**, **DomainSet**, and **ToolGroups** are configured at runtime allows developers to tailor the framework for desktop applications, headless APIs, or embedded harnesses without modifying core source code.

## ServiceSet: Configuring Transports and Background Services

The `ServiceSet` struct, defined in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) (lines 59‑65), controls which background services and transports are initialized when the core starts. Each field is a Boolean flag representing a specific capability such as HTTP servers, Socket.IO, cron schedulers, or channel listeners.

### Available Presets

The builder provides preset constructors for common deployment scenarios:

- **`ServiceSet::desktop()`** – Enables the full suite including HTTP server, WebSocket transports, and system tray integration.
- **`ServiceSet::headless_api()`** – Configures only the HTTP transport layer suitable for cloud deployments.
- **`ServiceSet::none()`** – Disables all transports for library-only usage.
- **`ServiceSet::embedded()`** – Starts background processing loops without exposing RPC endpoints.

```rust
use openhuman_core::{CoreBuilder, ServiceSet};

// Configure for a headless server environment
let services = ServiceSet::headless_api();
let builder = CoreBuilder::default().with_services(services);

```

The flags are consumed during `CoreBuilder::build()`, where [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) wires the selected transports to the appropriate background loops.

## DomainSet: Enabling Domain Families

`DomainSet`, located in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) immediately following ServiceSet (lines 59‑70), determines which domain families are registered with the runtime. Each Boolean field corresponds to a `DomainGroup` variant defined in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs), controlling the availability of controllers, stores, and event subscribers.

### DomainGroup Variants

The runtime recognizes distinct domain families including:

- **agent** – Agent lifecycle management and state machines
- **memory** – Vector stores and episodic memory systems
- **threads** – Conversation thread management
- **config** – Dynamic configuration stores
- **security** – Authentication and authorization layers
- **flows** – Workflow execution engines
- **skills** – Skill registries and loaders
- **mcp** – Model Context Protocol handlers
- **channels** – Message bus implementations
- **web3** – Blockchain interaction layers
- **voice** – Speech-to-text and text-to-speech services
- **media** – Media processing pipelines

### Preset Configurations

The framework provides three primary presets:

```rust
use openhuman_core::DomainSet;

// Enable every domain family (default desktop behavior)
let full = DomainSet::full();

// Minimal core for embedded agents: agent + memory + threads + config + security
let harness = DomainSet::harness();

// Disable all domain-specific controllers
let empty = DomainSet::none();

```

The chosen set is passed to `CoreBuilder::with_domains()`, which registers only the selected controllers with the dependency injection container.

## ToolGroups: Controlling Tool Visibility

ToolGroups manage the runtime visibility of agent tool collections through [`src/openhuman/tools/toolpacks/groups.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/toolpacks/groups.rs). Unlike ServiceSet and DomainSet, which use Boolean flags, ToolGroups employ a three-state enum (`GroupMode`) to control how tool schemas are exposed on the wire.

### GroupMode States

Each tool group identified by `GroupId` operates in one of three modes:

- **`Advertised`** – Tool schemas appear on the `/schema` endpoint and the tools are callable by agents.
- **`Withheld`** – Schemas are hidden from discovery, but the tools remain callable internally (useful for private agent capabilities).
- **`Off`** – The entire group is omitted; neither schemas nor execution endpoints are registered.

### Runtime Registration

Configure tool visibility before building the core:

```rust
use openhuman_core::tools::toolpacks::{ToolGroups, GroupId, GroupMode};

// Start with all groups packed (available but not necessarily advertised)
let mut groups = ToolGroups::packed();

// Hide web3 tools entirely for this deployment
groups = groups.with(GroupId::Web3, GroupMode::Off);

// Withhold system tools from schema endpoints but keep them callable
groups = groups.with(GroupId::System, GroupMode::Withheld);

let builder = CoreBuilder::default().with_tool_groups(groups);

```

The `ToolGroups` configuration is stored in [`src/core/runtime/context.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/context.rs) within the `CoreContext` structure, making the visibility settings available to the HTTP schema endpoint at runtime.

## Complete Integration Example

Combine all three configuration axes to create a minimal embedded harness:

```rust
use openhuman_core::{
    CoreBuilder,
    ServiceSet,
    DomainSet,
    tools::toolpacks::{ToolGroups, GroupId, GroupMode},
};

let core = CoreBuilder::default()
    // No network transports for embedded library use
    .with_services(ServiceSet::none())
    // Only core agent domains required for headless operation
    .with_domains(DomainSet::harness())
    // Configure tool visibility: hide web3, advertise agent tools
    .with_tool_groups(
        ToolGroups::packed()
            .with(GroupId::Web3, GroupMode::Off)
            .with(GroupId::Agent, GroupMode::Advertised)
    )
    .build()
    .await
    .expect("failed to build core");

```

This configuration creates a lightweight runtime suitable for embedding in third-party applications, with all blockchain tooling removed and only the essential service layer active.

## Summary

- **ServiceSet** in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) controls transport initialization via Boolean flags and presets like `desktop()` and `headless_api()`.
- **DomainSet** in the same file enables domain families (agent, memory, web3, etc.) through the `DomainGroup` registry in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs).
- **ToolGroups** in [`src/openhuman/tools/toolpacks/groups.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/toolpacks/groups.rs) manages tool visibility using `GroupMode` states (Advertised, Withheld, Off) rather than simple Booleans.
- All three configurations are applied through `CoreBuilder` methods (`with_services`, `with_domains`, `with_tool_groups`) and stored in [`src/core/runtime/context.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/context.rs) for runtime access.
- Presets allow rapid configuration for common scenarios while retaining granular control for specialized deployments.

## Frequently Asked Questions

### How do I disable all network transports in OpenHuman?

Pass `ServiceSet::none()` to the builder. This disables HTTP servers, WebSockets, and Socket.IO while keeping background processing loops available, as implemented in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) lines 59‑65.

### What is the difference between DomainSet::full() and DomainSet::harness()?

`DomainSet::full()` activates every domain family including web3, voice, and media processing, suitable for full desktop applications. `DomainSet::harness()` limits the runtime to only agent, memory, threads, config, and security domains, creating a minimal core for embedded usage as defined in the builder presets.

### Can I hide tool schemas but still allow agents to call the tools?

Yes. Set the tool group to `GroupMode::Withheld` in [`src/openhuman/tools/toolpacks/groups.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/toolpacks/groups.rs). This mode removes the schemas from the `/schema` endpoint while keeping the execution endpoints active, allowing internal agents to invoke the tools without exposing them to external discovery.

### Where are the ServiceSet and DomainSet stored after the core is built?

Both configurations are stored in the `CoreContext` struct defined in [`src/core/runtime/context.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/context.rs). The runtime uses this context to check which services should be started and which domain controllers should receive events during the application lifecycle.