How to Add a New Domain Module to the OpenHuman Rust Core (Including Controller Schemas)
To add a new domain module to the OpenHuman Rust core, create a directory under src/openhuman/, implement business logic in ops.rs, define JSON-RPC controller schemas in schemas.rs, expose them via mod.rs, and register the domain in 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– Re-exports and public API surfaceschemas.rs–ControllerSchemadefinitions andRegisteredControllerlistops.rs(orrpc.rs) – Business logic functions that handlers call- Optional:
types.rs,bus.rs, orcli.rsdepending on domain complexity
Implement the Business Logic
Write core operations in ops.rs using pure Rust async functions. These functions should return RpcOutcome<T> and may interact with databases or external services.
// src/openhuman/your_domain/ops.rs
pub async fn do_thing(arg: String) -> RpcOutcome<YourResult> {
// Business logic implementation
}
Define Controller Schemas
In 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.
// 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>(¶ms, "arg")?;
to_json(crate::openhuman::your_domain::ops::do_thing(arg).await?)
})
}
Expose the Module Publicly
Create mod.rs to re-export your schema functions and operations, following the pattern used in src/openhuman/cron/mod.rs:
// 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 and add your domain to both registry functions. Locate fn build_registered_controllers() and extend the controllers vector:
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:
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 implementing run_standalone_subcommand, then register it in core/all.rs’s cli_adapters() list.
Test and Validate Your Domain
Follow the existing test patterns in src/openhuman/cron/schemas.rs to write unit tests for schema generation and integration tests for handlers. Run the full validation suite:
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 with your domain’s purpose. Extend the namespace_description function in 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>/withmod.rs,schemas.rs, andops.rs - Implement async business logic in
ops.rsreturningRpcOutcome<T> - Define
ControllerSchemaobjects inschemas.rswith input/outputFieldSchemadefinitions - Register handlers via
all_registered_controllers()using theRegisteredControllerstruct with schema and handler pairs - Extend
src/core/all.rsregistry functionsbuild_registered_controllers()andbuild_declared_controller_schemas()to include your domain - Validate with
cargo testand document in architecture guides according to thecrondomain pattern
Frequently Asked Questions
What is the difference between ops.rs and schemas.rs in an OpenHuman domain?
ops.rs contains pure business logic implementations—the async functions that perform actual operations like database queries or computations. 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. 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 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 within a #[cfg(test)] module, following the pattern in src/openhuman/cron/schemas.rs. For integration tests, verify the handler logic connects properly to your ops.rs functions and handles parameter extraction correctly using read_required::<T>().
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →