Durable by default
Queue publish confirmations wait for the message and the state needed to recover it to cross the persistence boundary.
broker / queue / durable delivery
Fibril is a lightweight message broker focused on durable queues, explicit delivery semantics, leasing, retries, and predictable operational behavior, with fan-out streams alongside the work queues when you need broadcast. Most applications need durable queues, predictable delivery semantics, reconnect behavior that doesn't surprise them, and operational controls that are easy to understand during incidents. Fibril optimizes for that path. It is implemented in Rust, but the identity is simple, inspectable broker behavior. It is still early, and the missing pieces are named rather than hidden.
Durability is core and tested. Production readiness is still earned, not claimed.
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?,
};
} await foreach (var msg in sub.Deliveries())
{
try
{
await SendEmailAsync(msg.Text());
}
catch (EmailException error)
{
if (error.IsTemporary)
msg.RetryAfter(TimeSpan.FromSeconds(30));
else
msg.Fail();
continue;
}
msg.Complete();
} for msg := range sub.Deliveries {
sendErr := sendEmail(msg.Text())
var settleErr error
switch {
case sendErr == nil:
settleErr = msg.Complete()
case isTemporary(sendErr):
settleErr = msg.RetryAfter(30 * time.Second)
default:
settleErr = msg.Fail()
}
if settleErr != nil {
return settleErr
}
} for await (const msg of sub) {
try {
await sendEmail(msg.text());
} catch (error) {
if (isTemporary(error)) {
await msg.retryAfter(30_000); // milliseconds
} else {
await msg.fail();
}
continue;
}
await msg.complete();
} async for msg in sub:
try:
await send_email(msg.text())
except TemporaryEmailError:
await msg.retry_after(30) # seconds
except EmailError:
await msg.fail()
else:
await msg.complete() Worker loops with manual acknowledgements. Email sending and error types are application code.
Queue publish confirmations wait for the message and the state needed to recover it to cross the persistence boundary.
Complete, retry, delay, or fail work without building broker rituals into your app.
Pull-based delivery and bounded prefetch make pressure visible.
Plexus streams broadcast each record to every subscriber, alongside the work queues, with durability tiers you opt into.
current shape
The broker has a useful baseline today. The table separates available behavior from areas that still need tooling or client polish.
Read project status →| Feature | Status | Notes |
|---|---|---|
| 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. |
Append-only log, snapshot, and replay.
Custom TCP protocol with Rust, TypeScript, Python, Go, and C# clients.
ACK, fail, immediate retry, and delayed retry paths.
Broker path and Rust, TypeScript, Python, Go, and C# client methods are wired.
Per-message or per-queue default. Expired messages drop or dead-letter.
Global and per-queue policy are configurable. Replay tooling is still early.
Pull-based delivery and bounded prefetch.
Lazy loading and idle eviction are wired. Observability is still growing.
Browse active queue messages from admin tooling, with optional payload previews.
Multiple partitions per queue, with client-side key routing and transparent fan-in.
Fan-out channel: every subscriber receives every record, with durable named cursors and durability tiers you opt into.
Subscribe to every queue or stream matching a wildcard, and auto-attach new ones as they appear. Client-side.
Cluster nodes split a queue's active traffic by owning different partitions. Experimental.
Followers keep partition copies for failover and recovery. Experimental.
Not planned. Transactional workflows are intentionally excluded.
durable queue throughput
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 →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 →