broker / queue / durable delivery

Useful messaging without broker ceremony.

Fibril is a lightweight message broker for durable work queues and fan-out streams. A publish is confirmed once it is on disk. Workers lease messages, then complete, retry or delay them. Broker state stays plain and inspectable during incidents.

Early-stage. Durability is core and tested. Production readiness is still earned.

  1. Publish
  2. Message and enqueue state reach disk
  3. Only then is it confirmed
  4. A worker leases it, then acks or retries

With replication, the confirm also waits until the required replicas persist it.

view
use fibril_client::SubEvent;
use std::time::Duration;

while let SubEvent::Delivery(msg) = sub.recv().await {
    match send_email(msg.text()?).await {
        Ok(()) => msg.complete().await?,
        Err(err) if err.is_temporary() => {
            msg.retry_after(Duration::from_secs(30)).await?
        }
        Err(_) => msg.fail().await?,
    };
}

Worker loops with manual acknowledgements. Email sending and error types are application code.

inside a cluster

Copied before it is confirmed. Recovered after a crash.

Clustering is experimental. These scenes walk through the real protocol one step at a time. Timing is illustrative, not measured.

INSIDE FIBRIL / THE MESSAGE PATHCurrent main

Follow one message. Watch the waits overlap.

Illustrative time units, not benchmark results. The replicated examples require owner + one durable follower. Payload and enqueue-event dependencies both matter. Polling/wakeup and batching delays are simplified.

01

Follow the story

Illustrative time units, not benchmark results. The replicated examples require owner + one durable follower. Payload and enqueue-event dependencies both matter. Polling/wakeup and batching delays are simplified.

Illustrative time units, not benchmark results. The replicated examples require owner + one durable follower. Payload and enqueue-event dependencies both matter. Polling/wakeup and batching delays are simplified.

Read the story, step by step

Durable replication · Current main

  1. A publish enters the owner

    The owner must preserve both the payload and its enqueue event.

  2. Local persistence is in progress

    Appending and fsyncing establish the owner’s durable boundary.

  3. The follower can read the durable batch

    This animation shows the data response. The current transport is follower-pull.

  4. The follower persists its own copy

    The payload-bearing queue log must be durable and applied before reporting progress.

  5. Durable progress reaches the owner

    The exact payload/enqueue dependency and assignment epoch are checked.

  6. The durability requirement is satisfied

    Delivery and publisher confirmation can proceed independently.

  7. The worker acknowledges processing

    Processing ACK and publisher confirmation are different signals.

Early replication · Experimental · opt-in setting

  1. A publish enters the owner

    The durable confirmation contract stays the same.

  2. Local I/O starts

    The owner’s fsync is still pending.

  3. A written batch becomes readable early

    The experimental path can begin replica work before local fsync finishes.

  4. Two persistence windows overlap

    Overlap can remove serial waiting. Network, batching and progress-report costs remain.

  5. The follower has durable evidence

    The owner still verifies both dependencies and its own durability.

  6. Confirm and deliver after the durable boundary

    Early replication changes scheduling, not the required durable copies.

  7. The worker sends a processing ACK

    This signal does not retroactively replace the durability policy.

More scenes in the docs: placement, checkpoints, rebalancing →

crash test

Kill it mid-flight. The message is still there.

fibrilctl prints the offset only after the broker has the message on disk. Kill the broker with SIGKILL, start it again, and the message is waiting. In a cluster the confirm also waits for the required replicas, so a surviving node can take over.

~/fibril-demo
 

Recorded from fibril-server and fibrilctl built from source, on a scratch data dir with the default config. Log lines are trimmed: no timestamps, thread ids or file paths, module paths cut to their last segment, low-value fields dropped.

01

Durable by default

Queue publish confirmations wait for the message and the state needed to recover it to cross the persistence boundary.

02

Clients that read like intent

Complete, retry, delay, or fail work without building broker rituals into your app.

03

Lease under load

Pull-based delivery and bounded prefetch make pressure visible.

04

Fan-out streams

Plexus streams broadcast each record to every subscriber, alongside the work queues, with durability tiers you opt into.

current shape

Working parts, plainly stated.

The broker has a useful baseline today. The table separates available behavior from areas that still need tooling or client polish.

Read project status →
FeatureStatusNotes
Durable queues available Append-only log, snapshot, and replay.
Publish and subscribe available Custom TCP protocol with Rust, TypeScript, Python, Go, and C# clients.
Explicit settlement available ACK, fail, immediate retry, and delayed retry paths.
Delayed publish available Broker path and Rust, TypeScript, Python, Go, and C# client methods are wired.
Message TTL available Per-message or per-queue default. Expired messages drop or dead-letter.
Dead lettering available Global and per-queue policy are configurable. Replay tooling is still early.
Backpressure available Pull-based delivery and bounded prefetch.
Sparse queues available Lazy loading and idle eviction are wired. Observability is still growing.
Message inspection available Browse active queue messages from admin tooling, with optional payload previews.
Partitioned queues available Multiple partitions per queue, with client-side key routing and transparent fan-in.
Plexus streams available Fan-out channel: every subscriber receives every record, with durable named cursors and durability tiers you opt into.
Wildcard subscribe available Subscribe to every queue or stream matching a wildcard, and auto-attach new ones as they appear. Client-side.
Partition ownership experimental Cluster nodes split a queue's active traffic by owning different partitions. Experimental.
Replication experimental Followers keep partition copies for failover and recovery. Experimental.
Transactions out of scope Not planned. Transactional workflows are intentionally excluded.

Durable queues

available

Append-only log, snapshot, and replay.

Publish and subscribe

available

Custom TCP protocol with Rust, TypeScript, Python, Go, and C# clients.

Explicit settlement

available

ACK, fail, immediate retry, and delayed retry paths.

Delayed publish

available

Broker path and Rust, TypeScript, Python, Go, and C# client methods are wired.

Message TTL

available

Per-message or per-queue default. Expired messages drop or dead-letter.

Dead lettering

available

Global and per-queue policy are configurable. Replay tooling is still early.

Backpressure

available

Pull-based delivery and bounded prefetch.

Sparse queues

available

Lazy loading and idle eviction are wired. Observability is still growing.

Message inspection

available

Browse active queue messages from admin tooling, with optional payload previews.

Partitioned queues

available

Multiple partitions per queue, with client-side key routing and transparent fan-in.

Plexus streams

available

Fan-out channel: every subscriber receives every record, with durable named cursors and durability tiers you opt into.

Wildcard subscribe

available

Subscribe to every queue or stream matching a wildcard, and auto-attach new ones as they appear. Client-side.

Partition ownership

experimental

Cluster nodes split a queue's active traffic by owning different partitions. Experimental.

Replication

experimental

Followers keep partition copies for failover and recovery. Experimental.

Transactions

out of scope

Not planned. Transactional workflows are intentionally excluded.

durable queue throughput

Persisted. Confirmed. Still fast.

By default, a queue publish confirmation means the message and its enqueue state have crossed the persistence boundary through fsync. Replicated durable policies also wait for the required replicas to persist them.

Explore confirmation guarantees → See how durable writes stay fast →
250k+messages/sec ingress
250k+messages/sec egress

Observed on a single node with 1 KiB payloads on a Ryzen 5950X, using the durable queue path. These figures measure ingress and delivery throughput. The historical workload does not await a confirmation for every publish. Workload, results and measurement details →