Manage the long-running loops in your tokio application.
service-daemon-rs lets you declare the independent loops your program runs -- their dependencies, startup/shutdown order, failure recovery, and signal handling -- so your main.rs doesn't grow into a 500-line tokio::spawn graveyard.
It earns its keep when your application has more than one long-running concern. Typical scenarios:
- Protocol bridges and IoT gateways -- HTTP <-> MQTT, WebSocket <-> Redis stream, multi-protocol device front ends.
- Backends with rich background work -- scheduled cleanup, cache warmers, queue consumers, webhook receivers, health monitors alongside your HTTP API.
- Edge / robotics / industrial control -- sensor pipelines, camera + inference + uplink, control loops with strict startup/shutdown ordering.
- Any tokio application where you find yourself rewriting the same
select! { ... shutdown ... } + restart + backoffglue in every project.
- Declarative orchestration -- Describe services, triggers, providers, and their relationships with attributes like
#[service]or#[trigger(Cron(CleanupSchedule))], where trigger targets are provider types. No manual wiring inmain, no spawn-and-pray. - Production patterns built in -- Exponential backoff with jitter, wave-based startup/shutdown by priority, scheduling lanes (
Standard,HighPriority,Isolated), restart policies, graceful signal handling, early-binding TCP/Unix listeners -- the glue you'd otherwise rewrite per project. - Type-safe dependency injection -- Resolved by Rust's type system. No runtime container, no string keys, no reflection. Discovery is linker-level via
linkme. - Causal observability -- UUID v7 message IDs propagate across services automatically. Optional Mermaid topology export visualizes the running system.
- Testable by design -- A feature-gated
MockContextlets you simulate async behavior and state transitions in a controlled sandbox without spinning up the full daemon.
use service_daemon::prelude::*;
use service_daemon::{ServiceDaemon, provider, service, sleep};
use tracing::info;
use std::sync::Arc;
// 1. Define an injectable provider with a default value
#[derive(Clone)]
#[provider(8080)]
pub struct Port(pub i32);
// 2. Define a managed service using proc-macros
#[service]
pub async fn heartbeat_service(port: Arc<Port>) -> anyhow::Result<()> {
while !is_shutdown() {
info!("Heartbeat: service is alive on port {}", port);
// Interruptible sleep: returns false if shutdown is requested
if !sleep(std::time::Duration::from_secs(5)).await {
break;
}
}
Ok(())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 3. Build and run the daemon
let mut daemon = ServiceDaemon::builder().build();
daemon.run().await;
daemon.wait().await?;
Ok(())
}The Quick Start Guide walks through the framework one concept at a time:
- Hello, Heartbeat! -- Your first service.
- Reactive Triggers -- Events, queues, and chained handlers.
- State Management & Recovery -- Persistence across restarts.
- Sequential Startup, Shutdown & Scheduling -- Priority waves and runtime scheduling policies.
- See the Full Guide for the complete chapter list.
The examples/ directory contains focused examples organized by use case:
| Example | Focus | Run Command |
|---|---|---|
| minimal | is_shutdown() polling -- simplest pattern |
cargo run -p example-minimal |
| complete | state() lifecycle -- recovery, reload, priorities |
cargo run -p example-complete |
| triggers | Decoupled event-driven handlers (Cron, Queue, Watch) | cargo run -p example-triggers |
| logging | File-based JSON log persistence (file-logging feature) |
cargo run -p example-logging |
| diagnostics | Behavioral Topology and Mermaid export (diagnostics feature) |
cargo run -p example-diagnostics |
| scheduling | Standard, HighPriority, and Isolated runtime lanes for services |
cargo run -p examples-scheduling |
| unix-domain-socket | Unix socket listener and connector pair | cargo run -p example-unix-domain-socket |
| simulation | MockContext for unit testing (simulation feature) |
cargo test -p example-simulation |
Important: Do NOT mix
is_shutdown()polling (minimal) withstate()lifecycle matching (complete) in the same service. These are two independent control-flow paradigms.
Documentation is split by audience.
For people building applications on top of the framework.
- State Management -- Providers, mutability, zero-copy snapshots.
- Event Triggers -- Cron, queues, watchers.
- Resilience & Lifecycle -- Restart policy, jitter, wave-based orchestration.
- Priorities & Scheduling -- Startup/shutdown priority waves and runtime lanes.
- Diagnostics & Logs -- The
DaemonLayerfor runtime visibility. - Testing & Troubleshooting -- Mocking, FAQ.
For people extending the framework or debugging its internals.
- Internal Overview -- Registry design, linkme segments, DI resolution.
- The Ripple Model -- Causal tracing across asynchronous trigger chains.
- Lifecycle Deep Dive -- Reload paths and supervisor internals.
- Macros Mechanics -- How
#[service]/#[trigger]rewrite your code. - Extending the Framework -- Adding new trigger types or providers.
Licensed under either of
at your option.