- Rust 100%
jsonrpsee::core already re-exports async_trait. The prelude now re-exports it as hero_rpc2::async_trait so downstream can write #[async_trait] without adding async-trait to their Cargo.toml. Examples and tests updated. |
||
|---|---|---|
| docs | ||
| examples | ||
| src | ||
| tests | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
hero_rpc2
Lean JSON-RPC 2.0 framework for MOS daemons. Thin layer over
jsonrpsee 0.26 that ships the
Unix-socket transports and OpenRPC discovery that jsonrpsee doesn't
provide out of the box — and nothing else.
┌────────────────────────┐
│ Your daemon │
│ #[rpc] trait + impl │
└────────────┬───────────┘
│
┌────────────┴───────────┐ line transport (default)
│ hero_rpc2::ServerBuilder│────► POST / (uds-http, opt-in)
│ serve_line / serve_http│
└────────────┬───────────┘
│
Unix socket
│
┌────────────┴───────────┐
│ hero_rpc2::Client │ typed or raw JSON calls
└────────────────────────┘
This crate replaces the heavier hero_rpc (axum + tower-http + ACL +
tenancy + OSchema codegen) for MOS daemons. See docs/PRD.md for
design rationale and the non-goals below.
Quickstart
[dependencies]
hero_rpc2 = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
The prelude re-exports #[rpc], #[async_trait], RpcResult,
ErrorObject, RpcModule, and the client/server types — no need to
pull jsonrpsee or async-trait directly.
use hero_rpc2::prelude::*;
#[rpc(server, client)]
pub trait Echo {
#[method(name = "echo")]
async fn echo(&self, msg: String) -> RpcResult<String>;
}
struct EchoImpl;
#[async_trait]
impl EchoServer for EchoImpl {
async fn echo(&self, msg: String) -> RpcResult<String> { Ok(msg) }
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let sock = "/run/echo.sock";
let module = EchoImpl.into_rpc();
let server = ServerBuilder::new(module).serve_line(sock).await?;
let client = ClientBuilder::new().connect_line(sock).await?;
let out: String = EchoClient::echo(&*client, "hi".into()).await?;
println!("{out}");
server.shutdown().await;
Ok(())
}
Run the in-tree demos:
cargo run --example echo_line
cargo run --example echo_discover
cargo run --example echo_http --features uds-http
Features
| Feature | Default | What it enables |
|---|---|---|
uds-line |
✓ | Line-delimited JSON-RPC over a Unix socket (serve_line). |
uds-http |
HTTP/1.1 over a Unix socket via hyper 1.x (serve_http). |
|
discover |
✓ | rpc.discover + OpenRpcBuilder (schemars integration). |
client |
✓ | Client + ClientBuilder on top of jsonrpsee. |
Compile the smallest useful server (line-only, no discover, no client):
cargo build --no-default-features --features uds-line
Architecture at a glance
src/server.rs—ServerBuilderwrapsjsonrpsee::RpcModuleand adds chainablewith_discover(doc)and transport-specificserve_*methods. Modules are registered using jsonrpsee's nativeregister_{method, async_method, blocking_method}.src/client.rs—Clientderefs tojsonrpsee::core::client::Client, soClientT::requestworks directly.connect_line/connect_httpwire the sender/receiver halves throughClientBuilder::build_with_tokio.src/transport/line.rs—tokio::net::UnixListener→BufReader::lines→RpcModule::raw_json_request. Notifications are handled by injecting anullid pre-dispatch and suppressing the response.src/transport/http.rs—hyper::server::conn::http1overtokio::net::UnixListener.POST /only,application/json, no chunked encoding, 4 MiB default body limit.src/discover.rs— hand-rolled OpenRPC 1.2.6 builder withschemarsv1 integration (upstream jsonrpsee PR #1608 is still WIP).
Wire shape
Line (default): one JSON value per line. Batches are arrays.
Notifications omit id and yield no response line. No framing beyond \n.
HTTP (uds-http): POST / with Content-Type: application/json.
Body is one JSON-RPC request or batch. Notifications return HTTP 204.
Bad path → 404; wrong content-type → 400. No multi-tenant routing —
path segments deliberately dropped vs hero_rpc's
POST /{prefix}/{context}/{domain}/rpc/.
Non-goals
These made hero_rpc heavy; keeping them out is the point. If a feature
request lands in these categories, reject it or split to a separate crate.
- ACL / auth / session / identity / multi-tenancy / ContextRegistry
- Persistent storage
- Schema DSL or
.oschemacodegen hero_proclifecycle integration- HTTP routing beyond UDS framing
- Backward-compat replacement of
hero_rpcfor non-MOS consumers
Development
cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo test --features uds-http
Dependency budget: the workspace CI greps the transitive tree for forbidden crates. The following must be empty:
cargo tree | grep -E 'axum|tower-http|hero_|herolib_'
Documents
docs/PRD.md— product requirements and scopedocs/adr/001-architecture-and-public-api.md— core design, public API, featuresdocs/adr/002-http-over-uds-transport.md— uds-http transport
MSRV & license
Rust 1.85 (Rust 2024 edition). Apache-2.0.