# Understanding OpenHuman Feature Gates and Cargo Features: A Two-Level Architecture

> Explore OpenHuman's two-level feature gate architecture. Learn how Cargo features and runtime Domain gates optimize binary size while enabling optional capabilities.

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

---

**OpenHuman implements a dual-layer gating system that combines compile-time Cargo features with runtime Domain gates to minimize binary size while supporting optional capabilities like voice, web3, and media generation.**

The `tinyhumansai/openhuman` repository employs a sophisticated feature management strategy that separates what gets compiled from what gets exposed at runtime. Understanding OpenHuman feature gates and Cargo features is essential for developers embedding the core library or contributing to the desktop application.

## The Two-Level Feature Model

OpenHuman distinguishes between **Contributor** and **Product** feature sets to balance development convenience against shipping constraints.

### Contributor vs. Product Features

- **Contributor (Default) Features**: Defined in the `[features] default` section of [`Cargo.toml`](https://github.com/tinyhumansai/openhuman/blob/main/Cargo.toml), this set controls what compiles during `cargo check` or `cargo test`. It includes the kernel surface and inexpensive optional dependencies for local development.

- **Product Features**: Defined in [`scripts/ci/product-features.txt`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/product-features.txt) and the root [`Cargo.toml`](https://github.com/tinyhumansai/openhuman/blob/main/Cargo.toml), this set determines what ships in the desktop application. It includes heavier dependencies such as `libgit2-sys`, `zstd-sys`, and user-facing domain implementations.

The product feature set is forwarded to the Tauri shell in [`app/src-tauri/Cargo.toml`](https://github.com/tinyhumansai/openhuman/blob/main/app/src-tauri/Cargo.toml) to ensure the desktop build matches the core library exactly.

## Runtime Domain Gates

Beyond compile-time flags, the core implements a **runtime `DomainSet`** that filters entire domain families at program start.

### The DomainSet Builder API

Each domain corresponds to a directory under `src/openhuman/` (e.g., `voice`, `web3`, `flows`). The builder API allows embedders to select presets:

```rust
let harness = Harness::builder()
    .domains(DomainSet::full())      // all domains enabled
    .domains(DomainSet::harness())  // only core agent + memory + threads
    .build()
    .await?;

```

When a domain is disabled, its RPC controllers are completely omitted (clients receive *unknown-method* errors) and associated agent tools disappear from the tool list. Unlike simple `#[cfg]` guards, the code path is absent rather than guarded by a runtime check.

## How Features and Domains Interact

**Feature gates** compile-time-remove code and transitive dependencies, while **Domain gates** filter already-compiled controllers at runtime.

A domain is usually tied to a feature:

- **`voice` feature**: Enables the `voice` domain with STT/TTS providers, dictation servers, and podcasts (note: local Whisper was removed; STT now requires hosted providers).

- **`web3` feature**: Enables the `web3` domain with multi-chain crypto wallets, EIP-712 signing, and Solana RPC endpoints.

- **`media` feature**: Enables the `media` domain for image and video generation tools (agents only, no controllers).

- **`flows` feature**: Enables the `flows` domain with the workflow engine (`tinyflows`), Rhai scripting, and 25+ workflow-related agent tools.

- **`modules` feature**: Enables the `modules` domain for dynamic native modules (`tinydocs`, `tinymemory`, etc.) loaded at runtime via the TinyBus ABI in [`src/openhuman/modules/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/modules/registry.rs).

When a feature is disabled, the corresponding domain is automatically excluded from `DomainSet` presets. Conversely, a domain can be turned off at runtime even if its feature is compiled in.

## Required-Features for Compile-Time Exclusion

Some domains use Cargo’s `required-features` entry to force crate omission when a feature is off. For example, the `memory-git` domain adds `git2` and `libz-sys`; when the `memory-git` feature is omitted, tests depending on these crates are automatically skipped. This prevents compilation errors for developers who do not need Git-based memory features.

## CI Enforcement and Feature Forwarding

The repository includes a **feature-forwarding check** in `scripts/ci/check-feature-forwarding.mjs` that verifies:

1. The desktop shell forwards exactly the product feature list to the core.
2. Every name in the product list is a real core feature.
3. Any default-on feature not meant for the product is either forwarded or explicitly whitelisted.

If forwarding drifts (e.g., a feature disappears from the product build), the CI job fails.

## Kernel Floor Ratchet

The [`scripts/kernel-floor.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/kernel-floor.sh) tool measures the minimal dependency footprint for the **kernel profile** (`--no-default-features --features flows`). The target is approximately 302 packages and 2 native builds. Adding a feature that drastically increases this count requires a deliberate decision and an update to the floor limits file.

## Practical Implementation Examples

### Building a Harness with Custom Domains

```rust
use openhuman_core::{Harness, DomainSet, ServiceSet};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Enable only core agent, memory, and threads domains
    let harness = Harness::builder()
        .domains(DomainSet::harness())
        // Disable background services (no HTTP server, socket.io, etc.)
        .services(ServiceSet::none())
        .build()
        .await?;

    let response = harness.run("Summarize the Open Human architecture.").await?;
    println!("{}", response.output);
    Ok(())
}

```

`DomainSet::harness()` selects a minimal set (`Agent`, `Memory`, `Threads`, `Config`, `Security`), omitting `voice`, `web3`, and `flows` controllers as defined in [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs).

### Enabling Features at Build Time

```bash

# Build full desktop app with voice and web3

cargo build --release --features "voice web3"

```

This compiles the corresponding submodules in `src/openhuman/voice/` and `src/openhuman/web3/`, exposing their RPC namespaces and agent tools.

### Compiling a Minimal Kernel

```bash

# Compile only kernel profile

cargo build --release --no-default-features --features "flows"

```

This disables default-on contributor features (e.g., `rusqlite`, `ring`) while keeping the `flows` domain, resulting in approximately 300 packages as enforced by the kernel-floor check.

## Summary

- **Cargo features** in [`Cargo.toml`](https://github.com/tinyhumansai/openhuman/blob/main/Cargo.toml) control compilation and third-party dependencies.
- **Domain gates** in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs) and [`src/core/runtime/builder.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/runtime/builder.rs) filter runtime exposure of RPC endpoints and agent tools.
- **CI checks** enforce feature forwarding consistency and kernel size constraints.
- Adding domains requires updating `DomainGroup` enums, preset builders, and CI validation scripts.

## Frequently Asked Questions

### What is the difference between Cargo features and Domain gates in OpenHuman?

Cargo features determine what code is compiled and which dependencies are linked, controlled via [`Cargo.toml`](https://github.com/tinyhumansai/openhuman/blob/main/Cargo.toml) and `required-features`. Domain gates are runtime filters implemented in `DomainSet` that determine which RPC controllers and agent tools are active after compilation.

### How do I build a minimal OpenHuman kernel without heavy dependencies?

Use `cargo build --release --no-default-features --features "flows"` to compile only the kernel profile. This excludes heavy dependencies like `libgit2-sys` and `rusqlite`, keeping the binary near the 302-package floor limit enforced by [`scripts/kernel-floor.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/kernel-floor.sh).

### Why does the CI fail when I add a new feature to Cargo.toml?

The `scripts/ci/check-feature-forwarding.mjs` script validates that the desktop shell forwards exactly the product feature list. If you add a feature to the core but do not update the forwarding rules or whitelist it as a default-only feature, the CI will fail to prevent accidental omission from shipped builds.

### Can I disable a domain at runtime even if its feature is compiled in?

Yes. The `DomainSet` builder API allows runtime filtering of domains regardless of compile-time features. For example, you can compile with `--features "voice web3"` but instantiate `Harness` with `DomainSet::harness()` to disable voice and web3 endpoints at runtime while keeping the binary size.