Skip to main content

MeshDaemon

Trait MeshDaemon 

Source
pub trait MeshDaemon: Send + Sync {
    // Required methods
    fn name(&self) -> &str;
    fn requirements(&self) -> CapabilityFilter;
    fn process(
        &mut self,
        event: &CausalEvent,
    ) -> Result<Vec<Bytes>, DaemonError>;

    // Provided methods
    fn required_capabilities(&self) -> CapabilitySet { ... }
    fn optional_capabilities(&self) -> CapabilitySet { ... }
    fn snapshot(&self) -> Option<Bytes> { ... }
    fn is_stateful(&self) -> bool { ... }
    fn restore(&mut self, state: Bytes) -> Result<(), DaemonError> { ... }
    fn health(&self) -> DaemonHealth { ... }
    fn saturation(&self) -> f32 { ... }
    fn on_control(&mut self, _event: DaemonControl) { ... }
}
Expand description

A daemon that runs on the mesh.

Daemons consume inbound causal events via process() and return zero or more output payloads. The runtime wraps outputs in CausalLinks automatically — the daemon only produces raw payloads.

§Performance

process() must complete in microseconds. Heavy work should be deferred to a background task and emitted as a later event.

§WASM compatibility

All methods are synchronous — no async. Input/output are Bytes — maps cleanly to WASM linear memory. No generics or associated types.

Required Methods§

Source

fn name(&self) -> &str

Human-readable name (for logging, placement ads).

Source

fn requirements(&self) -> CapabilityFilter

Capability requirements for placement.

The scheduler uses this to find nodes whose CapabilitySet matches. Return CapabilityFilter::default() to run anywhere.

Source

fn process(&mut self, event: &CausalEvent) -> Result<Vec<Bytes>, DaemonError>

Process one inbound causal event, returning zero or more output payloads.

The output Bytes values become payloads in the daemon’s own causal chain (the runtime wraps them in CausalLinks automatically).

Provided Methods§

Source

fn required_capabilities(&self) -> CapabilitySet

Hard capability requirements for Phase F-aware placement.

Returns the set of tags + metadata the candidate node MUST have for the daemon to run there. Tag-set inclusion is the hard-constraint check (StandardPlacement returns None when a required tag is absent — see crate::adapter::net::behavior::placement).

Default: empty set. Daemons that care about specific hardware / software override:

fn required_capabilities(&self) -> CapabilitySet {
    CapabilitySet::new().add_tag("hardware.gpu")
}

Phase G slice 2 of CAPABILITY_SYSTEM_PLAN.md. Coexists with the legacy requirements() method until the mikoshi-placement-v2 feature flag flips: requirements() drives the legacy CapabilityFilter-based path; this method drives the Artifact::Daemon { required, .. } payload that PlacementFilter impls consume.

Source

fn optional_capabilities(&self) -> CapabilitySet

Soft capability preferences for Phase F-aware placement.

Returns the set of tags + metadata the daemon prefers but does NOT require. The scheduler factors satisfaction of these into per-axis scoring; missing optional capabilities don’t veto placement (unlike required_capabilities).

Default: empty set. Daemons with a strict required floor but additional preferences (e.g. “must have GPU; prefer 80GB+ VRAM”) populate this via per-tag adds.

Phase G slice 2 of CAPABILITY_SYSTEM_PLAN.md. Slice 5’s per-axis scorers consume the optional set when scoring candidates; slice 2’s stub axes return 1.0 regardless.

Source

fn snapshot(&self) -> Option<Bytes>

Serialize current state for migration/checkpoint.

Returns None for stateless daemons. Stateful daemons must return opaque bytes that restore() can accept.

Source

fn is_stateful(&self) -> bool

Whether this daemon carries persistent state that migration / restart paths must preserve.

The default restore previously accepted any bytes silently for daemons that didn’t override it, including ones that should have been stateful but forgot to provide a restore impl. The new default restores correctly: it matches is_stateful()’s answer. Stateless daemons leave is_stateful at false (matches snapshot() = None); stateful daemons override is_stateful to true AND snapshot / restore.

The migration path can use this to refuse to migrate a stateful daemon’s snapshot bytes into a stateless target, surfacing the misconfiguration rather than silently dropping state.

Source

fn restore(&mut self, state: Bytes) -> Result<(), DaemonError>

Restore from a previous snapshot.

Called before any process() calls after migration.

The default implementation now refuses non-empty state on stateless daemons (is_stateful() == false) — silently discarding a stateful source’s snapshot into a stateless target loses every byte of state with no signal. Stateful daemons must override both is_stateful and restore. An empty state is still accepted (it’s what snapshot() -> None produces under the migration adapter), so genuine stateless-to-stateless migrations continue to work.

Source

fn health(&self) -> DaemonHealth

Self-reported health. Polled by the MeshOS supervisor on each tick. Default Healthy.

Daemons with a real health surface (queue-depth probes, internal cache freshness, dependency readiness, etc.) override to return a richer value. The supervisor surfaces the latest sample on the behavior snapshot for Deck.

Must complete in microseconds — same constraint as process(). Heavy probes belong in a side task whose result the daemon caches.

Source

fn saturation(&self) -> f32

Self-reported saturation, 0.0 (idle) to 1.0 (fully loaded). Used by Phase D-1’s mesh scheduler to decide whether a daemon’s host is a good candidate for new work. Default 0.0.

Daemons without a meaningful saturation surface should leave the default. The value is informational under the current scheduler; a poor estimate doesn’t cause migrations to thrash.

Source

fn on_control(&mut self, _event: DaemonControl)

Receive a control event from the supervisor. Default: no-op (the daemon proceeds as normal regardless of the control signal).

Daemons that participate in graceful shutdown / drain / backpressure override to react. The dispatch is sync — the supervisor calls this between process() events on the daemon’s main task, so long-running work in on_control blocks subsequent event processing.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§