https://github.com/webermarci/roar
Distributed pub/sub for Gleam with automatic cluster synchronization across BEAM nodes
https://github.com/webermarci/roar
distributed gleam pubsub
Last synced: 15 days ago
JSON representation
Distributed pub/sub for Gleam with automatic cluster synchronization across BEAM nodes
- Host: GitHub
- URL: https://github.com/webermarci/roar
- Owner: webermarci
- License: mit
- Created: 2026-02-09T16:41:48.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-02-10T18:20:11.000Z (4 months ago)
- Last Synced: 2026-03-18T19:10:36.087Z (3 months ago)
- Topics: distributed, gleam, pubsub
- Language: Gleam
- Homepage: https://hex.pm/packages/roar
- Size: 8.79 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# roar
[](https://hex.pm/packages/roar)
[](https://hexdocs.pm/roar/)


> ⚠️ **Warning:** This project is experimental and unstable. APIs may change significantly between versions.
Lightweight distributed pub/sub for Gleam on Erlang with automatic cluster synchronization.
Roar enables process-local and cluster-wide message broadcasting with automatic scope isolation, supporting both streaming subscriptions and callback-based handlers.
## Features
- 🌍 **Distributed by default** - Messages automatically sync across all nodes in your cluster
- 🎯 **Scope isolation** - Multiple independent pub/sub systems in the same cluster
- 📦 **Simple API** - Subscribe with callbacks or buffered streams
- 🛡️ **Memory safe** - Configurable buffer capacity prevents runaway memory growth
- ⚡ **BEAM native** - Built on Erlang's distribution primitives
## Installation
Add `roar` to your Gleam project:
```sh
gleam add roar
```
## Quick Start
```gleam
import roar
pub fn main() {
// Create a router with buffer capacity and scope
let router = roar.new(capacity: 100, scope: "my_app")
// Subscribe to a topic
let sub = roar.subscribe(router, "events")
// Publish a message (reaches ALL nodes in "my_app" scope)
roar.publish(router, "events", "Hello, cluster!")
// Receive messages
case sub.receive() {
Ok(message) -> io.println(message)
Error(Nil) -> io.println("No messages")
}
sub.cancel()
}
```
## Usage
### Callback-based subscriptions
```gleam
let cancel = roar.on(router, "notifications", fn(msg) {
io.println("Received: " <> msg)
})
roar.publish(router, "notifications", "New user signed up!")
cancel()
```
### Buffered subscriptions
```gleam
let sub = roar.subscribe(router, "events")
roar.publish(router, "events", "Event 1")
roar.publish(router, "events", "Event 2")
// Messages are buffered, read at your own pace
let assert Ok("Event 1") = sub.receive()
let assert Ok("Event 2") = sub.receive()
sub.cancel()
```
### Multiple topics
```gleam
let router = roar.new(capacity: 50, scope: "chat")
let messages = roar.subscribe(router, "messages")
let alerts = roar.subscribe(router, "alerts")
roar.publish(router, "messages", "Hello!")
roar.publish(router, "alerts", "Server restarting")
// Each subscription only receives its topic's messages
```
### Scope isolation
```gleam
// Different scopes don't interfere with each other
let app_router = roar.new(capacity: 100, scope: "app")
let admin_router = roar.new(capacity: 100, scope: "admin")
roar.subscribe(app_router, "events") // Only sees "app" scope
roar.subscribe(admin_router, "events") // Only sees "admin" scope
```
## How It Works
Roar uses [Whisper](https://hexdocs.pm/whisper/) for local pub/sub and Erlang's `pg` (process groups) for cluster-wide distribution. When you publish a message:
1. **Local delivery** - Delivered immediately to subscribers on the same node
2. **Remote delivery** - Forwarded to all nodes in the same scope via process groups
3. **No duplicates** - Each subscriber receives exactly one copy
The `capacity` parameter protects against memory issues by limiting buffered messages per subscription. If a subscriber is too slow, old messages are dropped to prevent memory exhaustion.