An approachable, asynchronous RPC toolkit for Rust, built around JSON-RPC 2.0-style messages.
DiceRPC gives you a small handler registry, HTTP, TCP, and WebSocket transports, batch requests, shared state, metrics, and examples you can build on. The project is under active development and welcomes contributors of every experience level.
Project status: DiceRPC is currently
0.1.0and is best suited to learning, prototypes, and internal experimentation. Its API may change as protocol compliance and production hardening improve. Read Security before exposing a server to untrusted networks.
- JSON-RPC services with custom asynchronous methods
- HTTP APIs with Axum
- Persistent request/response sessions over WebSocket
- Newline-delimited or length-prefixed TCP services
- Stateful services backed by the included in-memory store
- Batch request handlers
- CLI clients and integration tests
DiceRPC is useful when you need a small, customizable RPC layer rather than a full web framework:
- Internal service APIs — expose typed business operations to trusted services over HTTP or TCP.
- Blockchain tooling and simulators — prototype node-style methods, transaction flows, wallets, and test harnesses.
- Developer tools — control local processes, emulators, indexers, or background workers through a simple RPC interface.
- Protocol experiments — compare HTTP, WebSocket, newline-delimited TCP, and framed TCP behavior around the same method registry.
- Teaching and testing — demonstrate async handlers, middleware, shared state, batch calls, metrics, and graceful shutdown.
DiceRPC is not yet intended to replace a hardened public API gateway. Put internet-facing deployments behind TLS, authentication, authorization, and edge-level abuse controls.
- Async method registration and concurrent execution with Tokio
- HTTP endpoints at
/and/rpc - WebSocket endpoint at
/wsfor persistent JSON-RPC sessions - TCP transport with legacy newline-delimited and length-prefixed modes
- Single and batch request processing
- API-key middleware for HTTP headers, request parameters, and framed TCP requests
- Configurable body/frame, batch, connection/concurrency, and timeout limits
- Thread-safe in-memory account and transaction state
- Request counts, errors, latency metrics, and tracing
- Health and metrics endpoints for metrics-enabled HTTP servers
- Graceful shutdown support for the framed TCP server
- CLI server and client commands
- Examples and integration tests for the main workflows
- Rust 1.85 or newer (the crate uses Rust 2024 edition)
- Cargo
- Git, if you are cloning the repository
Clone and build the project:
git clone https://github.com/dicethedev/DiceRPC.git
cd DiceRPC
cargo buildStart the basic newline-delimited TCP server:
cargo run -- serverIn another terminal, call its ping method:
cargo run -- client --method pingExpected output:
Response: {"jsonrpc":"2.0","result":"pong","error":null,"id":1}
You can also pass JSON parameters:
cargo run -- client \
--method get_balance \
--params '{"address":"0x123abc"}'Start the CLI HTTP server:
cargo run -- http-serverThen send a request from another terminal:
curl --request POST http://127.0.0.1:3000/rpc \
--header 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"ping","params":{},"id":1}'The CLI HTTP server includes stateful demonstration methods and exposes:
POST /andPOST /rpcfor RPC requestsGET /healthfor a basic health responseGET /metricsfor an in-memory metrics snapshot
Run cargo run -- --help to see all CLI commands and options.
WebSocket support is optional. Start the CLI server with the feature enabled:
cargo run --features websocket -- websocket-serverConnect to ws://127.0.0.1:3001/ws with a WebSocket client and send one JSON-RPC request per text message:
{"jsonrpc":"2.0","method":"ping","params":{},"id":1}The connection stays open for more single or batch requests. Text messages and UTF-8 binary messages are accepted; responses are sent as text messages.
Create an RpcServer, register an async handler, and attach a transport:
use dice_rpc::{RpcErrorObj, RpcServer};
use dice_rpc::transport::HttpTransport;
use serde_json::{json, Value};
use std::sync::Arc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let server = Arc::new(RpcServer::new());
server
.register("greet", |params: Value| async move {
let name = params
.get("name")
.and_then(Value::as_str)
.ok_or_else(|| RpcErrorObj {
code: -32602,
message: "Missing or invalid 'name' parameter".into(),
data: None,
})?;
Ok(json!({ "message": format!("Hello, {name}!") }))
})
.await;
HttpTransport::new(server)
.serve("127.0.0.1:3000")
.await
}Call the method with:
curl --request POST http://127.0.0.1:3000/rpc \
--header 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"greet","params":{"name":"Dice"},"id":1}'Handlers receive a serde_json::Value and return Result<Value, RpcErrorObj>. This keeps the core API small while allowing every method to validate or deserialize its own parameter type.
A typical request looks like this:
{
"jsonrpc": "2.0",
"method": "get_balance",
"params": { "address": "0xAlice" },
"id": 1
}A successful response contains result:
{
"jsonrpc": "2.0",
"result": { "address": "0xAlice", "balance": "100000" },
"id": 1
}An unsuccessful response contains an error object instead. DiceRPC follows the main JSON-RPC request/response shape, but full specification compliance—including notification behavior and every standard error case—is still a work in progress.
The HTTP, WebSocket, and framed TCP paths accept multiple requests in one JSON array:
curl --request POST http://127.0.0.1:3000/rpc \
--header 'Content-Type: application/json' \
--data '[
{"jsonrpc":"2.0","method":"ping","params":{},"id":1},
{"jsonrpc":"2.0","method":"get_balance","params":{"address":"0xAlice"},"id":2}
]'Batch entries are processed concurrently. DiceRPC applies a default batch limit of 100; tune this and the concurrency limits for your workload before accepting untrusted traffic.
Network-facing transports have conservative defaults and builder methods for tuning them:
| Transport | Default limits | Builder methods |
|---|---|---|
| HTTP | 1 MiB body, 100 batch entries, 256 concurrent requests, 30-second timeout | with_max_body_size, with_max_batch_size, with_max_concurrency, with_request_timeout |
| WebSocket | 1 MiB message, 100 batch entries, 1,024 connections, 30-second timeout per message | with_max_message_size, with_max_batch_size, with_max_connections, with_request_timeout |
| Framed TCP | 1 MiB frame, 100 batch entries, 1,024 connections, 30-second timeout | with_max_frame_size, with_max_batch_size, with_max_connections, with_request_timeout |
These are application-level safeguards, not a replacement for proxy, firewall, and operating-system limits.
For HTTP, header authentication keeps credentials out of the JSON-RPC parameters:
export API_KEYS='replace-with-a-long-random-secret'
cargo run -- http-server --auth
curl --request POST http://127.0.0.1:3000/rpc \
--header 'Content-Type: application/json' \
--header 'x-api-key: replace-with-a-long-random-secret' \
--data '{"jsonrpc":"2.0","method":"ping","params":{},"id":1}'WebSocket supports ApiKeyInHeader during the HTTP upgrade handshake, which authenticates the connection before it opens. It also supports ApiKeyInParams for authenticating each message. Framed TCP uses ApiKeyInParams, so each request includes "api_key" in its params object.
API keys are compared in constant time. Authentication does not provide per-method authorization, TLS, identity roles, or key storage; applications must add those controls for their environment.
Two groups of handlers are included for learning and testing:
| Handler set | Methods |
|---|---|
| Basic | ping, get_balance, send_tx |
| Stateful | ping, get_balance, set_balance, transfer, get_transaction, confirm_transaction, get_transactions, list_accounts |
These methods contain demonstration logic, not a blockchain implementation. In particular, balances and transactions live only in memory and disappear when the process stops.
| Feature | Default | Purpose |
|---|---|---|
tcp |
Yes | TCP transport and framing |
http |
Yes | Axum HTTP transport and endpoints |
websocket |
No | WebSocket transport at /ws; also enables http |
full |
No | Explicitly enables all transports |
Useful build commands:
# Default build: HTTP and TCP
cargo build
# TCP only
cargo build --no-default-features --features tcp
# HTTP only
cargo build --no-default-features --features http
# WebSocket (and its HTTP foundation)
cargo build --no-default-features --features websocket
# All supported transports
cargo build --features fullThe examples directory is the fastest way to explore individual features:
| Example | Purpose |
|---|---|
http_basic |
Minimal HTTP server |
http_client |
HTTP client requests |
http_batch_requests |
HTTP batch calls |
http_with_auth |
Parameter-based API keys over HTTP |
http_with_header_auth |
Recommended x-api-key HTTP authentication |
http_with_no_auth |
Explicit unauthenticated HTTP setup |
http_with_state |
Shared application state over HTTP |
http_full_featured |
HTTP features used together |
production_http |
Environment-driven auth and resource limits |
websocket_server |
Persistent single and batch requests over WebSocket |
tcp_basic |
Minimal TCP server |
tcp_framed |
Length-prefixed TCP messages |
tcp_with_auth |
Parameter-based authentication over framed TCP |
tcp_with_state |
Shared application state over TCP |
tcp_client_advanced |
Advanced TCP client usage |
tcp_full_featured |
TCP features used together |
Run one with:
cargo run --example http_basic
# WebSocket is an optional feature
cargo run --example websocket_server --features websocketSome examples start a server and keep running until you stop them with Ctrl+C.
For a longer architecture walkthrough, see the DiceRPC implementation guide.
src/
├── client/ # CLI client
├── middleware/ # Authentication middleware
├── rpc/ # Request, response, and handler registry
├── server/ # Server helpers, stateful handlers, and metrics
├── transport/ # HTTP, WebSocket, TCP, framing, metrics, and shutdown
├── util/ # Batch request handling
├── lib.rs # Public library exports
├── main.rs # CLI entry point
├── macros.rs # Helper macros
└── state.rs # In-memory accounts and transactions
Run the same checks before submitting a pull request:
cargo fmt --all -- --check
cargo clippy --all-targets --all-features
cargo test --all-features
cargo build --examples --all-featuresWhen fixing a bug, add a regression test that fails without the fix. When adding a public feature, include an example or update this README so people can discover it.
Contributions are welcome—documentation fixes and focused beginner changes are just as valuable as large features. See CONTRIBUTING.md for development setup, quality checks, commit guidance, pull-request expectations, and licensing terms.
For significant features, security-sensitive work, or breaking API changes, please open an issue before implementation so the approach can be discussed.
DiceRPC is experimental and has not received an independent security audit. Read SECURITY.md for supported versions, known limitations, safer deployment guidance, and private vulnerability-reporting instructions.
Please report suspected vulnerabilities through GitHub Security Advisories, not a public issue.
The roadmap is intentionally flexible while the core API matures:
- Complete JSON-RPC 2.0 compliance tests
- Enforce authentication consistently across transports
- Add header-based HTTP authentication
- Add configurable request, batch, connection, and timeout limits
- Add per-method authorization hooks
- Add TLS and reverse-proxy deployment documentation
- Add persistent state adapters
- Add WebSocket transport
- Add Prometheus-compatible metrics
- Add fuzzing, benchmarks, and load tests
- Publish versioned API documentation and migration notes
Have another idea? Open an issue and describe the problem it solves.
DiceRPC is dual-licensed under your choice of:
- the MIT License; or
- the Apache License, Version 2.0.
You may use, modify, and distribute the project under either license. See the license files for the complete terms.
- JSON-RPC 2.0 specification
- Tokio documentation
- Axum documentation
- Serde documentation
- Rust API Guidelines
Built by dicethedev. If DiceRPC helps you learn or build something useful, consider starring the repository or contributing an improvement.