# How to Add a New Domain Module to the OpenHuman Rust Core (Including Controller Schemas)

> Learn how to add a new domain module to the OpenHuman Rust core. Discover steps for implementing business logic, defining controller schemas, and registering your new domain.

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

---

**To add a new domain module to the OpenHuman Rust core, create a directory under `src/openhuman/`, implement business logic in [`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs), define JSON-RPC controller schemas in [`schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/schemas.rs), expose them via [`mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/mod.rs), and register the domain in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs)’s global controller registry.**

OpenHuman’s Rust core is organized as a collection of **domain modules** located under `src/openhuman/`, each providing self-contained business logic and **controller schemas** that describe the public JSON-RPC surface. Adding a new domain follows a predictable pattern that ensures your code integrates seamlessly with the global controller registry and remains discoverable via the `/schema` endpoint.

## Initialize the Domain Structure

Create a new directory for your domain under `src/openhuman/` with the following required files:

- [`mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/mod.rs) – Re-exports and public API surface
- [`schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/schemas.rs) – `ControllerSchema` definitions and `RegisteredController` list
- [`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs) (or [`rpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/rpc.rs)) – Business logic functions that handlers call
- Optional: [`types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/types.rs), [`bus.rs`](https://github.com/tinyhumansai/openhuman/blob/main/bus.rs), or [`cli.rs`](https://github.com/tinyhumansai/openhuman/blob/main/cli.rs) depending on domain complexity

## Implement the Business Logic

Write core operations in [`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs) using pure Rust async functions. These functions should return `RpcOutcome<T>` and may interact with databases or external services.

```rust
// src/openhuman/your_domain/ops.rs
pub async fn do_thing(arg: String) -> RpcOutcome<YourResult> {
    // Business logic implementation
}

```

## Define Controller Schemas

In [`schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/schemas.rs), define a schema generator function and two helper exports that wire handlers to their JSON-RPC definitions. This file serves as the contract between your Rust implementation and the JSON-RPC interface.

```rust
// src/openhuman/your_domain/schemas.rs
use crate::core::{ControllerSchema, FieldSchema, TypeSchema};
use crate::core::all::{ControllerFuture, RegisteredController};

pub fn all_controller_schemas() -> Vec<ControllerSchema> {
    vec![
        schemas("do_thing"),
    ]
}

pub fn all_registered_controllers() -> Vec<RegisteredController> {
    vec![
        RegisteredController {
            schema: schemas("do_thing"),
            handler: handle_do_thing,
        },
    ]
}

fn schemas(function: &str) -> ControllerSchema {
    match function {
        "do_thing" => ControllerSchema {
            namespace: "your_domain",
            function: "do_thing",
            description: "Perform the core operation.",
            inputs: vec![
                FieldSchema {
                    name: "arg",
                    ty: TypeSchema::String,
                    comment: "Argument for the operation.",
                    required: true,
                },
            ],
            outputs: vec![
                FieldSchema {
                    name: "result",
                    ty: TypeSchema::Ref("YourResult"),
                    comment: "Result of the operation.",
                    required: true,
                },
            ],
        },
        _ => panic!("Unknown function: {}", function),
    }
}

fn handle_do_thing(params: Map<String, Value>) -> ControllerFuture {
    Box::pin(async move {
        let arg = read_required::<String>(&params, "arg")?;
        to_json(crate::openhuman::your_domain::ops::do_thing(arg).await?)
    })
}

```

## Expose the Module Publicly

Create [`mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/mod.rs) to re-export your schema functions and operations, following the pattern used in [`src/openhuman/cron/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/cron/mod.rs):

```rust
// src/openhuman/your_domain/mod.rs
mod ops;
mod schemas;

pub use ops::*;
pub use schemas::{
    all_controller_schemas as all_your_domain_controller_schemas,
    all_registered_controllers as all_your_domain_registered_controllers,
    schemas as your_domain_schemas,
};

```

## Register with the Global Controller Registry

Open [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs) and add your domain to both registry functions. Locate `fn build_registered_controllers()` and extend the controllers vector:

```rust
fn build_registered_controllers() -> Vec<RegisteredController> {
    let mut controllers = vec![];
    controllers.extend(crate::openhuman::your_domain::all_your_domain_registered_controllers());
    // ... existing domains
    controllers
}

```

Then locate `fn build_declared_controller_schemas()` and extend the schemas vector:

```rust
fn build_declared_controller_schemas() -> Vec<ControllerSchema> {
    let mut schemas = vec![];
    schemas.extend(crate::openhuman::your_domain::all_your_domain_controller_schemas());
    // ... existing domains
    schemas
}

```

This registration makes your RPC methods discoverable via the `/schema` endpoint and callable by the CLI and JSON-RPC server.

## Add Optional CLI Support

If your domain requires command-line access, create [`cli.rs`](https://github.com/tinyhumansai/openhuman/blob/main/cli.rs) implementing `run_standalone_subcommand`, then register it in [`core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/core/all.rs)’s `cli_adapters()` list.

## Test and Validate Your Domain

Follow the existing test patterns in [`src/openhuman/cron/schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/cron/schemas.rs) to write unit tests for schema generation and integration tests for handlers. Run the full validation suite:

```bash
cargo fmt          # Format Rust code

cargo check        # Compile-time verification

cargo test         # Run all tests including new domain tests

```

## Document the New Domain

Update [`gitbooks/developing/architecture.md`](https://github.com/tinyhumansai/openhuman/blob/main/gitbooks/developing/architecture.md) with your domain’s purpose. Extend the `namespace_description` function in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs) to include a description for your new namespace, ensuring the public API reference remains accurate.

## Summary

- **Create** a new directory under `src/openhuman/<your_domain>/` with [`mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/mod.rs), [`schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/schemas.rs), and [`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs)
- **Implement** async business logic in [`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs) returning `RpcOutcome<T>`
- **Define** `ControllerSchema` objects in [`schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/schemas.rs) with input/output `FieldSchema` definitions
- **Register** handlers via `all_registered_controllers()` using the `RegisteredController` struct with schema and handler pairs
- **Extend** [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs) registry functions `build_registered_controllers()` and `build_declared_controller_schemas()` to include your domain
- **Validate** with `cargo test` and document in architecture guides according to the `cron` domain pattern

## Frequently Asked Questions

### What is the difference between [`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs) and [`schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/schemas.rs) in an OpenHuman domain?

[`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs) contains pure business logic implementations—the async functions that perform actual operations like database queries or computations. [`schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/schemas.rs) defines the JSON-RPC interface layer, including `ControllerSchema` structures that describe method signatures and `RegisteredController` instances that map HTTP requests to your business logic functions via handler functions like `handle_do_thing`.

### How does the global controller registry discover my new domain methods?

The registry discovers methods through explicit registration in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs). You must extend both `build_registered_controllers()` and `build_declared_controller_schemas()` using the vectors returned by your domain’s `all_registered_controllers()` and `all_controller_schemas()` functions. Without these extensions, your domain remains unreachable via JSON-RPC despite being compiled into the binary.

### Can I use custom types in my controller schema definitions?

Yes. Use `TypeSchema::Ref("YourType")` in your `FieldSchema` definitions to reference custom types, as shown in the output field examples. Ensure these types are properly serialized and documented in your domain’s [`types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/types.rs) or shared type definitions so that the schema generator can resolve them.

### Where should I add tests for my new domain module?

Add unit tests for schema generation directly in [`schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/schemas.rs) within a `#[cfg(test)]` module, following the pattern in [`src/openhuman/cron/schemas.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/cron/schemas.rs). For integration tests, verify the handler logic connects properly to your [`ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/ops.rs) functions and handles parameter extraction correctly using `read_required::<T>()`.