No description
Find a file
Jan De Landtsheer 4c6c83cad3
refactor: drop direct async-trait dep, re-export from jsonrpsee
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.
2026-04-15 00:58:44 +02:00
docs refactor: drop direct async-trait dep, re-export from jsonrpsee 2026-04-15 00:58:44 +02:00
examples refactor: drop direct async-trait dep, re-export from jsonrpsee 2026-04-15 00:58:44 +02:00
src refactor: drop direct async-trait dep, re-export from jsonrpsee 2026-04-15 00:58:44 +02:00
tests refactor: drop direct async-trait dep, re-export from jsonrpsee 2026-04-15 00:58:44 +02:00
.gitignore chore: initialize hero_rpc2 crate 2026-04-14 23:17:23 +02:00
Cargo.lock refactor: drop direct async-trait dep, re-export from jsonrpsee 2026-04-15 00:58:44 +02:00
Cargo.toml refactor: drop direct async-trait dep, re-export from jsonrpsee 2026-04-15 00:58:44 +02:00
README.md refactor: drop direct async-trait dep, re-export from jsonrpsee 2026-04-15 00:58:44 +02:00

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.rsServerBuilder wraps jsonrpsee::RpcModule and adds chainable with_discover(doc) and transport-specific serve_* methods. Modules are registered using jsonrpsee's native register_{method, async_method, blocking_method}.
  • src/client.rsClient derefs to jsonrpsee::core::client::Client, so ClientT::request works directly. connect_line / connect_http wire the sender/receiver halves through ClientBuilder::build_with_tokio.
  • src/transport/line.rstokio::net::UnixListenerBufReader::linesRpcModule::raw_json_request. Notifications are handled by injecting a null id pre-dispatch and suppressing the response.
  • src/transport/http.rshyper::server::conn::http1 over tokio::net::UnixListener. POST / only, application/json, no chunked encoding, 4 MiB default body limit.
  • src/discover.rs — hand-rolled OpenRPC 1.2.6 builder with schemars v1 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 .oschema codegen
  • hero_proc lifecycle integration
  • HTTP routing beyond UDS framing
  • Backward-compat replacement of hero_rpc for 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 scope
  • docs/adr/001-architecture-and-public-api.md — core design, public API, features
  • docs/adr/002-http-over-uds-transport.md — uds-http transport

MSRV & license

Rust 1.85 (Rust 2024 edition). Apache-2.0.