Write Once Run Anywhere (WORA): A Rust framework for building applications (daemons, etc.) that run in different environments (Linux, Kubernetes, etc.). Just like Java's claim, it really doesn't run everywhere. no_std or embedded environments are not supported.
This crate is an early-stage async framework. The main public API is usable, with Unix-like platforms as the primary target and environment-specific executors for systemd, launchd, and container-style deployments.
- restart policies, shutdown supervision, and signal mapping can be applied with
RunnerOptionsthroughexec_async_runner_with_options - initial typed config and secret loading is built into the runner
- event-driven apps can use
Wora::run_event_loopto auto-apply typed reloads RunnerOptions::with_signal_mapper(...)lets apps map Unix-like signals to control or app-defined events- observability can be routed through sinks via
O11yProcessor
- abstracts over common boilerplate with an API
- execute the same code in different executors (with or without recompiling)
- translates platform-specific control inputs into runtime control or app-defined events
- watches existing metadata and secrets directories and emits typed reload events
- supervises readiness, health, and graceful shutdown
- exposes host, process, runtime, and tracing observability events
- ships observability sinks and a processor for fan-out
- async-based apps
- Unix-like environments
- Linux-specific host details through
procfs - macOS host details through
sysinfo
- Linux-specific host details through
- service-manager and orchestration integrations
SystemdExecutorLaunchdExecutorContainerExecutor
WORA has six main pieces:
App: the workload lifecycle trait. Applications implementsetup,main,reload_config,reload_secrets, andend.AsyncExecutor: the environment adapter. Executors provide directories, setup, readiness, and teardown behavior.Wora: the runtime context passed to apps. It carries directories, host data, event channels, runtime status, and observability options. Health and readiness are reported throughWora::report_health(...),Wora::report_readiness(...), or the clonedRuntimeStatusHandle.WFS: the virtual filesystem abstraction.PhysicalVFSis the host filesystem implementation.LockBackend: the single-instance coordination abstraction.ProcLockBackenduses host lock files andInMemoryLockBackendis useful for tests and fully virtual runner flows.O11yProcessor: the observability pipeline for fan-out into sinks.
exec_async_runner wires those pieces together by initializing observability, acquiring the runtime lock, building the Wora context, running executor setup, loading initial config and secrets, running app setup, watching the metadata and secrets directories, invoking App::main, supervising shutdown/readiness/health, and then running teardown. Runner-owned background work shares a cancellation token: teardown requests cooperative cancellation, waits for a bounded grace period, and aborts tasks that do not stop.
The runner always installs recursive watchers on both metadata_root_dir and secrets_root_dir after executor and app setup. Executors must create both watch roots even when an app uses NoConfig or NoSecrets; initial loading skips roots that are absent, but watcher installation still requires them. The default filesystem lock backend creates the parent of its lock path before acquiring the lock, allowing executor setup to create the remaining roots afterward.
Typed config and secret reloads are driven by filesystem watcher events (Event::ConfigChanged and Event::SecretChanged). ControlEvent::ReloadConfiguration, ControlEvent::Suspend, and ControlEvent::LogRotation are app-level control notifications: the runner delivers them to the app but does not automatically reload files, suspend work, or rotate sinks for the app. Use RunnerOptions::with_signal_mapper(...) when the default Unix-like signal mapping should produce app-defined events instead.
When a lock backend reports contention, the runner returns MainEarlyReturn::AlreadyRunning(...) instead of collapsing that case into a generic exit code.
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
- Rust 1.95 or newer. The crate declares
rust-version = "1.95"and the checked-inrust-toolchain.tomlpins1.95.0for repository builds.
Run the basic example:
cargo run --example basicRun the daemon-style event loop example:
cargo run --example async_daemon -- --run-mode userRun the Linux systemd daemon example:
cargo run --example systemd_daemonSample unit file:
examples/systemd_daemon.service
Run the onboarding example:
cargo run --example onboardingRun checks:
cargo fmt --check
cargo testOpenAI Codex is used to write documentation/tests and find issues. Everything is reviewed by me and I vouch for these AI-generated changes.
We use SemVer for versioning when x.y.z when x is > 0, otherwise versioning is arbitrary. For the versions available, see the tags on this repository.
- Adam Flott - Initial work - adamflott
See also the list of contributors who participated in this project.
This project is dual-licensed under either the MIT License or the Apache License 2.0, at your option.