Platform Adapters

Duende supports multiple platforms through the PlatformAdapter trait. All 6 adapters are fully implemented.

Supported Platforms

PlatformAdapterStatusFalsification
NativeNativeAdapterCompletecargo run --example daemon
Linux (systemd)SystemdAdapterCompletesystemctl --user status duende-*
macOS (launchd)LaunchdAdapterCompletelaunchctl list | grep duende
ContainerContainerAdapterCompletedocker ps | grep duende
pepita (MicroVM)PepitaAdapterCompletepepita list | grep duende-vm
WOSWosAdapterCompletewos-ctl ps | grep duende

Platform Detection

#![allow(unused)]
fn main() {
use duende_core::platform::{detect_platform, Platform};

let platform = detect_platform();
match platform {
    Platform::Linux => println!("Running on Linux (systemd)"),
    Platform::MacOS => println!("Running on macOS (launchd)"),
    Platform::Container => println!("Running in container"),
    Platform::PepitaMicroVM => println!("Running in pepita microVM"),
    Platform::Wos => println!("Running on WOS"),
    Platform::Native => println!("Native fallback"),
}
}

Automatic Adapter Selection

#![allow(unused)]
fn main() {
use duende_core::adapters::select_adapter;
use duende_core::platform::detect_platform;

// Auto-detect platform and get appropriate adapter
let platform = detect_platform();
let adapter = select_adapter(platform);

// Use the adapter
let handle = adapter.spawn(Box::new(my_daemon)).await?;
}

PlatformAdapter Trait

All adapters implement this trait:

#![allow(unused)]
fn main() {
#[async_trait]
pub trait PlatformAdapter: Send + Sync {
    /// Returns the platform this adapter handles
    fn platform(&self) -> Platform;

    /// Spawns a daemon and returns a handle
    async fn spawn(&self, daemon: Box<dyn Daemon>) -> PlatformResult<DaemonHandle>;

    /// Sends a signal to the daemon
    async fn signal(&self, handle: &DaemonHandle, sig: Signal) -> PlatformResult<()>;

    /// Returns current daemon status
    async fn status(&self, handle: &DaemonHandle) -> PlatformResult<DaemonStatus>;

    /// Attaches a tracer for syscall monitoring
    async fn attach_tracer(&self, handle: &DaemonHandle) -> PlatformResult<TracerHandle>;
}
}

Supported Signals

#![allow(unused)]
fn main() {
pub enum Signal {
    Term,   // SIGTERM (15) - graceful shutdown
    Kill,   // SIGKILL (9)  - force kill
    Int,    // SIGINT (2)   - interrupt
    Quit,   // SIGQUIT (3)  - quit with core dump
    Hup,    // SIGHUP (1)   - reload config
    Usr1,   // SIGUSR1 (10) - user-defined
    Usr2,   // SIGUSR2 (12) - user-defined
    Stop,   // SIGSTOP (19) - pause
    Cont,   // SIGCONT (18) - resume
}
}

Native Adapter

The NativeAdapter is the fallback for all platforms. It spawns daemons as regular OS processes using tokio::process:

#![allow(unused)]
fn main() {
use duende_core::adapters::NativeAdapter;
use duende_core::types::Signal;

let adapter = NativeAdapter::new();
let handle = adapter.spawn(Box::new(my_daemon)).await?;

// Check status
let status = adapter.status(&handle).await?;
println!("Status: {:?}", status);

// Send graceful shutdown
adapter.signal(&handle, Signal::Term).await?;
}

Handle Types

Each adapter returns platform-specific handle data:

#![allow(unused)]
fn main() {
pub enum HandleData {
    Native { pid: u32 },
    Systemd { unit_name: String },
    Launchd { label: String },
    Container { runtime: String, container_id: String },
    Pepita { vm_id: String, vsock_cid: u32 },
    Wos { pid: u32 },
}
}

Error Handling

All adapter operations return PlatformResult<T>:

#![allow(unused)]
fn main() {
pub enum PlatformError {
    SpawnFailed(String),
    SignalFailed(String),
    StatusFailed(String),
    TracerFailed(String),
    NotSupported(String),
    Config(String),
}
}