# Arbiter: a Rust actor framework for simulations and networked systems

> Arbiter is an event-driven multi-agent framework in Rust built on a lightweight actor model, with traits for lifecycle, message handling, networking and runtime routing, and a workspace aimed at discrete-event simulation, automated trading and distributed systems. It is published under AGPL-3.0, and its release tags trail the code in the repository.

**harnesslabs/arbiter** — Multi-agent framework for design, simulation, and auditing.

- Repository: https://github.com/harnesslabs/arbiter
- Stars: 746 · Forks: 76
- Language: Rust
- License: AGPL-3.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/harnesslabs-arbiter

## A lightweight actor model aimed at simulation, not general concurrency

Arbiter is a Rust-based, event-driven multi-agent framework that lets developers orchestrate strongly-typed, high-performance simulations and networked systems. The readme puts the target audience plainly: discrete-event simulation, automated trading, and complex distributed systems.

That targeting matters, because an actor framework is a bet on how you structure concurrency. Arbiter says it is designed around a lightweight actor model, and it provides the foundational types and traits rather than a batteries-included runtime. If you are building a simulation where thousands of agents exchange typed messages, that model fits. If you are building a web service, the same traits can express it, but you will be doing more of the wiring yourself.

The project sits under the Harness Labs organisation, and the documentation is an MDBook hosted at arbiter.harnesslabs.dev, with the readme pointing there for an in-depth dive into the design, extensive examples and API specifics. The repository ships a book.toml, a docs/ directory and an examples/ directory holding a chat example and a leader example, so the design discussion is versioned alongside the code.

The type safety claim is the one to test. In a simulation, a message type mismatch is a modelling error that shows up as wrong behaviour rather than a compile error, so a framework that enforces message types at the trait level is buying something real.

## The five traits that make up the whole API

The readme lists the architecture as several key traits and structs, and there are only five. That is worth noting, because a small trait surface is the design, not an omission.

Actor is the core execution unit, containing the agent's internal state and logic. LifeCycle is a trait defining the start, stop and snapshot behaviour of an actor, and it is associated with three types in the example: a Snapshot type, a StartMessage type and a StopMessage type. Handler<M> is the trait used to implement message-handling logic for a specific message type, with an associated Reply type. Network is a generalized trait for instantiating and managing connections between parts of the system, which is where the pluggable networking comes from. Runtime manages the execution context of the actors, mediating subscriptions and routing messages.

The separation between Runtime and Network is the interesting structural choice. The runtime is the message bus and scheduler, and the network is an abstraction over connections, so an actor does not know whether the message it received came from a local queue or a socket. That is what lets the same actor definition run inside a single-process simulation and inside a distributed deployment, which is the whole point for the automated trading and distributed systems cases.

The readme is explicit that the project provides the foundational types and traits, and the rest is on you. There is no mention of persistence, clustering, timers or supervision strategies in what is published here, and a simulation framework without a timer is a significant gap, so the documentation site is the place to check what actually exists.

## Writing a counter actor: lifecycle and handler in one file

The quick example is worth reading in full, because it shows the shape of every actor you would write. The actor is a plain struct with public state, and the message is a unit struct:

```rust
use arbiter::prelude::*;

#[derive(Debug, Clone)]
pub struct Ping;

#[derive(Debug, Clone)]
pub struct Counter {
    pub count: usize,
}
```

Then the lifecycle implementation, which is where the associated types come from:

```rust
impl LifeCycle for Counter {
    type Snapshot = usize;
    type StartMessage = ();
    type StopMessage = ();

    fn on_start(&mut self) -> Self::StartMessage {}
    fn on_stop(&mut self) -> Self::StopMessage {}
    fn snapshot(&self) -> Self::Snapshot { self.count }
}
```

And the message handler, which takes the message by reference and returns an optional reply:

```rust
impl Handler<Ping> for Counter {
    type Reply = ();

    fn handle(&mut self, _message: &Ping) -> Option<Self::Reply> {
        self.count += 1;
        println!("Counter received Ping. Total: {}", self.count);
        None
    }
}
```

Three things stand out. The handler returns Option, so a message that expects no answer returns None and a request that expects one returns Some, which is how a typed request and response fits into a fire-and-forget message system. The snapshot returns the count as a usize, so the lifecycle trait is not just start and stop hooks, it is the mechanism for capturing an actor's state, which is what you need for checkpointing a simulation. And the prelude import means the common types come in one glob, so the traits you implement are in scope without naming their paths.

## The workspace, the pinned version, and the release tags that lag behind

The repository is a Cargo workspace with three members, the core crate and the two examples:

```toml
[workspace]
members  = ["arbiter", "examples/leader", "examples/chat"]
resolver = "2"

[workspace.dependencies]
arbiter = { path = "./arbiter", version = "=0.6.0" }
```

That version field is the interesting line. The workspace dependency pins the crate to exactly 0.6.0, while the published release tags stop at arbiter-v0.4.20 from 2024-04-26, with arbiter-v0.4.19 from 2024-02-26 before it and arbiter-bindings-v0.1.7 from the same April 2024 date. There is also a separate arbiter-bindings crate with its own version line, as a distinct artifact.

So the code in the repository is at 0.6.0 and the last thing published to a registry is 0.4.20. If you depend on the published crate you are two minor versions behind the source tree, and the README does not document a migration path between them.

The release profile is tuned for runtime performance rather than compile time, with codegen-units = 1 and lto = true, which is the right trade for a simulation framework where the binary is long-lived and the hot path is message handling.

The build tooling is a justfile with colour-coded headers, and it installs the toolchain a contributor needs: cargo-udeps for unused dependency detection, cargo-semver-checks for API compatibility, and taplo-cli for TOML formatting.

## The AGPL-3.0 licence is the adoption decision

The licence is AGPL-3.0, and for a framework meant to be embedded in networked systems, that is the first thing to check rather than the last.

The practical effect of the GNU Affero General Public License is that it covers network use. A service that links Arbiter and is reachable over a network is required to offer its corresponding source to users of that service. For a company building an internal simulation tool, that is usually not a problem. For a company shipping Arbiter inside a product, a hosted platform or an agent platform offered to third parties, the obligations are considerably heavier than MIT or Apache-2.0 would be.

This is a licensing question and not a legal opinion, and the practical point is timing. Choosing a framework is cheap; discovering the licence terms when you are already deep in an integration is not. A team that intends to commercialise on top of a multi-agent framework should resolve the licence question with whoever handles that, before the first line of code, because the alternative is a rewrite.

The rest of the repository is conventional. There is a .rustfmt.toml and a .taplo.toml for formatting, a Cargo.lock, a LICENSE file, a justfile, and the .github directory with contributing guidelines that the readme links. The project welcomes community contributions.

## Maintenance: the last push is 2026-07-01 and the tags are from 2024

The repository is not archived, and the last push was on 2026-07-01. The most recent releases are arbiter-v0.4.20 on 2024-04-26, arbiter-bindings-v0.1.7 on 2024-04-26 and arbiter-v0.4.19 on 2024-02-26.

Put those two facts together and the picture is a project that is still being worked on but does not publish releases on the same rhythm. The source tree is ahead of the tags, at 0.6.0 against a published 0.4.20, and the release history shows multi-month gaps rather than a regular cadence.

For an evaluation that means two things. If you need a published, semver-stable crate to depend on, what is on the registry is two minor versions behind the code you can read, and the readme documents no upgrade notes between them. If you are willing to work against the repository directly, as a path dependency, you get the newer code and you take on the maintenance of an unversioned surface yourself.

The doc build is part of that setup. The justfile has recipes for installing mdbook and its plugins, mdbook-linkcheck among them, so the documentation is built and link-checked locally the same way CI would. That is a project treating its book as a first-class artefact, which counts for something when the README is as short as this one.

## Conclusion

Arbiter fits a Rust team building a discrete-event simulation, a trading system or a distributed service where actors with typed messages and explicit lifecycle are the right unit of design, and it does not fit a project that cannot accept AGPL-3.0 terms or that needs a published, semver-stable release to depend on. Before building on it, read the mdbook documentation, check the version the workspace pins against the published release tags, and decide early whether the AGPL-3.0 licence fits how you intend to distribute the result.

## FAQ

### What is the Arbiter framework and what is it for?

Arbiter is a Rust-based, event-driven multi-agent framework for orchestrating strongly-typed, high-performance simulations and networked systems. It is built around a lightweight actor model and is aimed at discrete-event simulation, automated trading and complex distributed systems.

### What traits does an Arbiter actor implement?

The readme lists five core pieces: Actor as the execution unit holding state and logic, LifeCycle for start, stop and snapshot behaviour, Handler<M> for message-handling logic per message type, Network for managing connections between parts of the system, and Runtime which manages the execution context and routes messages.

### How do I write an actor with Arbiter?

Define a struct for your state, implement LifeCycle with its Snapshot, StartMessage and StopMessage associated types, and implement Handler for each message type you accept, returning Option to signal whether a reply is expected. The readme's example shows a Counter actor with a Snapshot of usize and a Handler<Ping> that increments the count.

### What licence is Arbiter released under?

The project is licensed under AGPL-3.0. Because that licence covers network use, a service that links Arbiter and is reachable over a network has source offer obligations, so teams planning to commercialise on top of it should settle the licence question early rather than at release time.

### Where is the Arbiter documentation and its examples?

The documentation is an MDBook at arbiter.harnesslabs.dev, and the repository holds a book.toml, a docs directory and an examples directory with a chat example and a leader example. The build tooling in the justfile installs mdbook and mdbook-linkcheck so the book can be built and link-checked locally.

## Sources

- [harnesslabs/arbiter on GitHub](https://github.com/harnesslabs/arbiter)
- [Issues](https://github.com/harnesslabs/arbiter/issues)
- [License: AGPL-3.0](https://github.com/harnesslabs/arbiter/blob/main/LICENSE)
- [README](https://github.com/harnesslabs/arbiter/blob/main/README.md)
- [Releases](https://github.com/harnesslabs/arbiter/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/harnesslabs-arbiter
