Arbiter
Welcome to the Arbiter documentation!
What is Arbiter?
Arbiter is a Rust-based, event-driven multi-agent framework that lets developers orchestrate strongly-typed, high-performance simulations and networked systems.
Arbiter provides the foundational types and traits for building actor-based systems with pluggable networking and lifecycle management. It is designed around a lightweight actor model tailored for discrete-event simulation, automated trading, and complex distributed systems.
Why use Arbiter?
- High Performance: Built in Rust for speed and safety.
- Event-Driven Architecture: Easily handle concurrent, asynchronous events via message passing.
- Simplicity: Straightforward traits (
Actor,LifeCycle,Handler) reduce the boilerplate associated with distributed programming. - Extensible: Designed to scale from local in-memory simulations to full network deployments.
Getting Started
To get started with Arbiter, you will need to add it to your Cargo.toml.
Installation
Add the following to your Cargo.toml dependencies:
[dependencies]
arbiter = "0.5.0"
First Steps
Once you have Arbiter installed, the typical flow for setting up a simulation is:
- Initialize the Runtime: The runtime is the execution environment for your actors.
- Define an Actor: Create a struct for your agent and implement the
LifeCycleandHandlertraits. - Spawn the Actor: Use the runtime to spawn your actor into the environment.
- Send Messages: Interact with your actors by sending them messages through their addresses.
Let’s explore these concepts in more detail in the next chapter.
Core Concepts
Arbiter’s design hinges on a few central abstractions that make setting up complex, event-driven interactions straightforward and efficient.
Actor
At the heart of Arbiter is the Actor. An actor encapsulates state and behavior. It is the fundamental unit of computation that communicates with other actors exclusively through message passing.
Actors in Arbiter are defined by a standard Rust struct that implements a few key traits.
LifeCycle
The LifeCycle trait defines the core temporal stages of an actor’s existence:
on_start(): Called when the actor is spawned into the runtime. Ideal for initialization tasks or broadcasting startup events.on_stop(): Called during shutdown. Use this for cleanup and persistence.snapshot(): Produces a snapshot of the actor’s current state, which is incredibly useful for simulation logging, testing, or building replays.
Handler
While LifeCycle defines how an actor lives and dies, the Handler<M> trait defines how it interacts with the world. You implement Handler<M> for each message type M your actor needs to understand.
#![allow(unused)]
fn main() {
// An example of an actor handling a specific `Tick` message.
impl Handler<Tick> for MyActor {
type Reply = Update;
fn handle(&mut self, message: &Tick) -> Option<Self::Reply> {
// Business logic here
Some(Update { ... })
}
}
}
The Network manages connections between actors and the broader system. It handles the low-level details of broadcasting messages and establishing direct communication channels (Sockets).
Arbiter provides two main implementations:
InMemory: High-performance, same-process communication using standard Rust channels.TcpStream: Distributed communication using TCP, enabling actors to span multiple processes and nodes. It features automatic mesh formation via a gossip protocol, ensuring all nodes in the network discover and connect to each other for seamless broadcasting and point-to-point communication. It also automatically resolves the local LAN IP for easy peer discovery.
This abstraction allows Arbiter systems to scale from local in-memory runners to distributed processing nodes.
Runtime
The Runtime provides the execution backdrop. It schedules the actors, mediates their inbox subscriptions, manages the network, and ensures that the system ticks along efficiently. When an actor is placed in a runtime, it is granted a Socket that it uses for receiving and emitting messages.
Examples
Learning by example is one of the best ways to get familiar with Arbiter.
Ping, Pong!
The classic Actor model example: Ping Pong. We want an actor that acts as a Counter and responds to Ping messages.
#![allow(unused)]
fn main() {
use arbiter::prelude::*;
#[derive(Debug, Clone)]
pub struct Ping;
#[derive(Debug, Clone)]
pub struct Counter {
pub count: usize,
}
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 }
}
impl Handler<Ping> for Counter {
type Reply = (); // We aren't replying to the ping here
fn handle(&mut self, _message: &Ping) -> Option<Self::Reply> {
self.count += 1;
println!("Received Ping! Count: {}", self.count);
None
}
}
}
The WASM Leader / Follower Example
Arbiter supports compiling to WebAssembly (wasm32-unknown-unknown). In the examples/leader/ directory, you’ll find a complete web application utilizing Arbiter actors to control UI components on an HTML canvas.
The example demonstrates:
- A
Leaderactor that moves autonomously. Followeractors that track and chase the leader.- An overarching
Canvasactor the manages rendering by interpreting thePositionUpdatemessages emitted by agents.
Running the Leader Example
To run the example locally, navigate to examples/leader and follow the README.md instructions there, or run the build directly using your preferred WASM server.
The TCP Chat CLI Example
Arbiter’s TcpStream network allows actors to communicate over a local network. The examples/chat/ directory contains a decentralized chat application that demonstrates:
- Distributed Actors: Actors on different nodes connecting and exchanging messages.
- Mesh Networking: Automatic peer discovery and connection via a gossip protocol.
- Dynamic Registration: Automatic type registration for complex serializable types across nodes.
- Socket Injection: Using
Runtime::socket()to bridge external input (keyboard/stdin) into the actor network.
Running the Chat Example
- Open a terminal and start the first node (Alice):
cargo run --example chat -- --name Alice - Alice will display her listening address (e.g.,
192.168.1.10:51234). - Open another terminal and start the second node (Bob), connecting to Alice:
cargo run --example chat -- --name Bob --connect <ALICE_IP>:<PORT> - Start a third node (Charlie) and connect it to either Alice or Bob. Thanks to the mesh gossip protocol, Charlie will automatically discover and connect to the other node, forming a complete three-way chat!
- Type messages in any terminal to see them delivered in real-time across the entire mesh!
Contributing to Arbiter
Thank you for your interest in contributing to Arbiter! This document provides guidelines and instructions for contributing to the project.
Table of Contents
- Code of Conduct
- Getting Started
- Development Workflow
- Documentation
- Issue Guidelines
- Pull Request Guidelines
- Code Style
- Testing
Code of Conduct
By participating in this project, you agree to abide by our Code of Conduct. Please be respectful and considerate of others.
Getting Started
- Fork the repository
- Clone your fork:
git clone https://github.com/yourusername/arbiter.git cd arbiter - Set up the development environment:
# Install Rust (if not already installed) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Install development tools rustup component add rustfmt clippy
Development Workflow
-
Create a new branch for your feature/fix:
git checkout -b type/area/description # Example: git checkout -b feat/algebra/vector-spaces -
Make your changes following the Code Style guidelines
-
Run tests and checks:
cargo test cargo fmt --all -- --check cargo clippy --all-targets --all-features -- -D warnings -
Commit your changes following the Commit Message Format
-
Push your branch and create a Pull Request
Documentation
Harness provides two types of documentation that you should be familiar with:
API Documentation
The Rust API documentation for all crates can be viewed using:
just docs
This will build and open the Rust API documentation in your browser. This documentation is automatically generated from your code comments and should be kept up to date.
Book Documentation
The comprehensive book documentation can be viewed using:
just book
This will serve the book documentation locally and open it in your browser. The book includes detailed explanations of mathematical concepts, examples, and usage guides.
When contributing, please:
- Keep API documentation up to date with your code changes
- Update the book documentation if you add new features or change existing behavior
- Add examples to both API docs and the book where appropriate
- Ensure mathematical definitions and references are accurate
Issue Guidelines
When creating issues, please use the provided templates and follow these guidelines:
Title Format
type(area): brief description
Where:
typeis one of:feat,fix,refactor,docs,test,choreareais one of:core,engine,ethereum,macros, etc.
Labels
Please use appropriate labels to categorize your issue:
- Area labels:
area: core,area: engine,area: ethereum,area: macros, etc. - Priority labels:
priority: critical/high/medium/low - Type labels:
type: enhancement,type: refactor - Technical labels:
tech: performance,tech: security,tech: testing
Pull Request Guidelines
- Use the provided PR template
- Ensure your PR title follows the format:
type(area): description - Link related issues using
closes #issue_number - Keep PRs focused and small when possible
- Include tests for new features or bug fixes
- Update documentation as needed
Code Style
- Follow Rust’s official style guide
- Use
rustfmtfor formatting - Run
cargo clippyto catch common mistakes - Document public APIs thoroughly
- Use meaningful variable and function names
- Keep functions focused and small
Testing
- Write unit tests for all new functionality
- Include examples in documentation
- Run all tests before submitting PRs
- Consider edge cases and error conditions
Commit Message Format
Follow this format for commit messages:
type(area): description
[optional body]
[optional footer]
Where:
typeis one of:feat,fix,refactor,docs,test,choreareais one of:core,engine, etc.- Description is a brief summary of changes
- Body provides additional context if needed
- Footer references issues or PRs
Questions?
If you have any questions, feel free to:
- Open an issue with the
questionlabel - Join our community discussions
- Contact the maintainers