# OpenHuman DomainSet Runtime Composition Pattern: How Gate Families Control Domain Availability

> Discover the OpenHuman DomainSet runtime composition pattern. Learn how gate families dynamically control domain availability at runtime for enhanced agent flexibility.

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

---

**The DomainSet runtime composition pattern in OpenHuman provides a boolean flag-based system where each gate family (agent, memory, flows, etc.) controls whether entire domain groups register their controllers, stores, and tools at runtime.**

The `tinyhumansai/openhuman` repository implements a sophisticated runtime composition model that allows embedders to precisely control which functional domains are active. Through the **DomainSet** struct defined in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs), developers can toggle entire families of functionality via simple boolean flags, creating custom runtime configurations ranging from full desktop shells to minimal kernel floors.

## The Three-Axis Runtime Composition Model

OpenHuman organizes its initialization into three independent composition axes that work orthogonally:

- **ServiceSet** – Controls background services and transport layers (HTTP server, Socket.IO, cron jobs)
- **DomainSet** – Determines which **gate families** (domain groups) are available at runtime
- **ToolGroups** – Configures how tools are exposed to the model (advertised, withheld, or disabled)

This separation allows you to combine any service configuration with any domain configuration. For example, you can run a headless HTTP API (`ServiceSet::headless_api()`) while enabling only the memory domain (`DomainSet` with `memory: true`), completely bypassing the agent, voice, or web3 families.

## DomainSet Struct and Gate Families

### Defining Gate Families in builder.rs

The **DomainSet** struct in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) (lines 52-72) defines a boolean flag for each functional family:

```rust
/// Selects which domain *families* exist at runtime on a [`CoreRuntime`] (#4796).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DomainSet {
    pub agent: bool,
    pub memory: bool,
    pub threads: bool,
    pub config: bool,
    pub security: bool,
    pub flows: bool,
    pub skills: bool,
    pub mcp: bool,
    pub channels: bool,
    pub web3: bool,
    pub voice: bool,
    pub media: bool,
    pub medulla: bool,
    pub inference: bool,
    pub integrations: bool,
    pub automation: bool,
    pub runtimes: bool,
    pub desktop: bool,
    pub hosted: bool,
    pub modules: bool,
    pub platform: bool,
}

```

Each field represents a **gate family**—a logical grouping of controllers, stores, subscribers, and tools. When a flag is `false`, the entire family remains unregistered, preventing any RPC methods, event handlers, or tools from that domain from entering the runtime.

### DomainGroup Enum and Permission Logic

The **DomainGroup** enum (defined in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs)) provides the typed interface for these families. The `DomainSet::allows()` method in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) (lines 87-111) performs the runtime check:

```rust
pub fn allows(&self, group: DomainGroup) -> bool {
    match group {
        DomainGroup::Agent => self.agent,
        DomainGroup::Memory => self.memory,
        // … all other groups …
        DomainGroup::Platform => self.platform,
    }
}

```

During `CoreContext::init_with_config`, registration code throughout the codebase calls `ctx.domains().allows(group)` before wiring any components. For instance, the flows controller only registers if `self.domains.flows` returns true, ensuring disabled families consume no runtime resources.

## Runtime Composition Presets

OpenHuman provides five preset configurations in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) (lines 122-176) covering common host shapes:

- **`DomainSet::full()`** – Enables every gate family; default for desktop shells
- **`DomainSet::harness()`** – Minimal agent-memory stack (`agent`, `memory`, `threads`, `config`, `security`); disables `platform` and heavy subsystems. Used by [`examples/embed_headless.rs`](https://github.com/tinyhumansai/openhuman/blob/main/examples/embed_headless.rs)
- **`DomainSet::embedded()`** – Long-lived embedder configuration adding Medulla, automation, integrations, and `platform` to the harness, while keeping `web3`, `voice`, and `media` disabled
- **`DomainSet::kernel()`** – Minimal kernel floor with only `threads`, `config`, and `security`; all large subsystems disabled
- **`DomainSet::none()`** – Disables every family, leaving only always-on core infrastructure

These presets return a `DomainSet` struct with specific boolean configurations, allowing you to start with a template and override individual flags as needed.

## Compile-Time vs Runtime Gating

Gate families operate at two levels. **Compile-time** gating occurs through Cargo feature flags (e.g., `voice`, `web3`, `media`), which control whether crates are included in the binary. The **DomainSet** pattern adds a runtime gate on top: even if a feature is compiled in, the corresponding domain remains inactive unless its `DomainSet` flag is `true`.

Conversely, if a feature is disabled at compile time, the associated flag cannot enable it—DomainSet only controls availability among compiled-in families. This dual-layer approach ensures binaries remain small while offering runtime flexibility for embedded scenarios.

## Practical Implementation Examples

### Headless API with Selective Domains

Create a headless server that only exposes the memory domain over HTTP:

```rust
use openhuman_core::core::runtime::{CoreBuilder, ServiceSet, DomainSet};

let core = CoreBuilder::new(HostKind::Desktop)
    .services(ServiceSet::headless_api())   // RPC HTTP only
    .domains(DomainSet {
        memory: true,                       // keep the memory domain
        ..DomainSet::none()                 // everything else off
    })
    .build()
    .await?;

```

### CLI Tool with Custom Skill Catalog

Embed the core in a CLI tool requiring agent capabilities and custom skills:

```rust
let core = CoreBuilder::new(HostKind::Cli)
    .services(ServiceSet::none())            // no background services
    .domains(DomainSet {
        agent: true,
        memory: true,
        threads: true,
        config: true,
        security: true,
        skills: true,        // enable skill runtime
        ..DomainSet::none()
    })
    .tool_groups(
        ToolGroups::default()
            .with("skills", GroupMode::Advertised)   // expose skill tools
    )
    .build()
    .await?;

```

### Minimal Kernel Floor

Use the kernel preset for resource-constrained embedded environments:

```rust
let core = CoreBuilder::new(HostKind::Embedded)
    .domains(DomainSet::kernel())   // only threads, config, security
    .services(ServiceSet::none())
    .build()
    .await?;

```

## Summary

- **DomainSet** in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) uses boolean flags to control twenty distinct gate families
- The `allows()` method checks `DomainGroup` values against these flags during `CoreContext` initialization
- **Presets** like `harness()` and `kernel()` provide common configurations for desktop, headless, and embedded hosts
- Runtime gating operates orthogonally to compile-time Cargo features, ensuring only enabled *and* compiled-in families register
- Registration logic in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs) verifies `ctx.domains().allows(group)` before wiring controllers, stores, or tools

## Frequently Asked Questions

### What is a gate family in OpenHuman?

A **gate family** is a logical grouping of related runtime components—controllers, stores, event-bus subscribers, and tools—identified by a single boolean flag in the `DomainSet` struct. Each family (like `agent`, `memory`, or `web3`) can be toggled independently to include or exclude entire functional domains from the core runtime without modifying source code.

### How does DomainSet interact with compile-time features?

**DomainSet** provides runtime gating while Cargo features provide compile-time gating. If a feature (e.g., `voice`) is disabled at compile time, its code is excluded from the binary and the corresponding `DomainSet` flag has no effect. If compiled in, the flag controls whether the family registers during `CoreContext::init_with_config`. This dual-layer system keeps binaries small while allowing runtime flexibility.

### What is the difference between ServiceSet and DomainSet?

**ServiceSet** controls background infrastructure—transports like HTTP servers, Socket.IO, cron schedulers, and notification channels—while **DomainSet** controls business logic domains (the actual features and capabilities). You can combine any ServiceSet preset with any DomainSet preset, such as running a headless API server (`ServiceSet::headless_api()`) with only the memory domain (`DomainSet` with `memory: true`).

### When should I use DomainSet::harness() versus DomainSet::embedded()?

Use **`DomainSet::harness()`** for minimal agent-memory testing environments where you need the core agent stack but no platform services or heavy subsystems. Use **`DomainSet::embedded()`** for long-lived production embedders that require additional capabilities like Medulla, automation, and integrations while keeping resource-intensive families (voice, media, web3) disabled to conserve memory and CPU.