murmer 0.1.2

A distributed actor framework for Rust
Documentation

Typed, location-transparent actors that communicate through message passing. Whether an actor lives in the same process or on a remote node across the network, you interact with it through the same Endpoint<A> API.

                          ┌─ local ──→ Actor (in-memory, zero-copy)
Your Code → Endpoint<A> ──┤
                          └─ remote ─→ QUIC stream (bincode, encrypted) ──→ Actor (remote node)

Highlights

Location-transparent Same Endpoint<A> API whether the actor is local or on another node
Minimal boilerplate #[handlers] generates message structs, dispatch tables, and extension traits from plain method signatures
Networking included QUIC transport, TLS encryption, SWIM membership, mDNS discovery — configured, not hand-rolled
OTP-style supervision Restart policies (Temporary, Transient, Permanent) with limits and exponential backoff
Test without infra Spin up multi-node clusters in-memory from a single process

Quick Start

[dependencies]
murmer = "0.1"
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }

Define an actor:

use murmer::prelude::*;

#[derive(Debug)]
struct Counter;
struct CounterState { count: i64 }

impl Actor for Counter {
    type State = CounterState;
}

#[handlers]
impl Counter {
    #[handler]
    fn increment(&mut self, _ctx: &ActorContext<Self>, state: &mut CounterState, amount: i64) -> i64 {
        state.count += amount;
        state.count
    }

    #[handler]
    fn get_count(&mut self, _ctx: &ActorContext<Self>, state: &mut CounterState) -> i64 {
        state.count
    }
}

Use it:

#[tokio::main]
async fn main() {
    let system = System::local();
    let counter = system.start("counter/main", Counter, CounterState { count: 0 });

    let result = counter.increment(5).await.unwrap();
    assert_eq!(result, 5);

    // Lookup by label — works for local and remote actors
    let looked_up = system.lookup::<Counter>("counter/main").unwrap();
    let count = looked_up.get_count().await.unwrap();
    assert_eq!(count, 5);
}

Go distributed — swap one line, your actor code stays the same:

// Before: let system = System::local();
let system = System::clustered(config, TypeRegistry::from_auto(), SpawnRegistry::new()).await?;

// Everything else is identical
let counter = system.start("counter/main", Counter, CounterState { count: 0 });
let remote = system.lookup::<Counter>("counter/on-other-node").unwrap();
remote.get_count().await?;  // transparently serialized over QUIC

Documentation

Resource Description
The Murmer Book User guide: actors, discovery, supervision, clustering, macros, orchestration
API Reference Rustdoc on docs.rs
Examples Runnable demos: counter, cluster chat, orchestrator

Build & Test

cargo build
cargo nextest run
cargo clippy -- -D warnings

Why murmer?

I've spent years working with Elixir and the BEAM VM, and the actor model there is something I've grown deeply fond of — the simplicity of processes, message passing, and supervision just works. When I looked at bringing that experience to Rust, the existing frameworks were impressive but getting a basic actor up and running was complex, and adding remote communication was even more so.

Murmer is an experiment in answering: can you build a robust distributed actor system in Rust that's actually simple to use? The design draws from BEAM OTP, Akka's clustering, and Apple's Swift Distributed Actors — combined with Rust's performance and safety guarantees.

License

Licensed under either of

at your option.