Skip to main content

DaemonRuntime

Struct DaemonRuntime 

Source
pub struct DaemonRuntime { /* private fields */ }
Expand description

Per-mesh compute runtime.

Holds the kind-keyed factory table, the per-daemon host registry, and the Registering → Ready → ShuttingDown lifecycle gate. One DaemonRuntime per Mesh; clone the handle freely — the inner state is Arc-shared.

Implementations§

Source§

impl DaemonRuntime

Source

pub fn new(mesh: Arc<Mesh>) -> Self

Attach a runtime to an existing Mesh. Stage 1 does not consume the Mesh — users keep their Arc<Mesh> for channel registration, subscription, and the rest of the non-compute surface. Stage 2 will install the migration subprotocol handler when Self::start runs; until then inbound migration messages (if any) are silently dropped by the core, same as today.

Source

pub fn register_factory<F>( &self, kind: &str, factory: F, ) -> Result<(), DaemonError>
where F: Fn() -> Box<dyn MeshDaemon> + Send + Sync + 'static,

Register a factory for a daemon type. kind is a user-chosen string shared across every node that may host this daemon. Second registrations of the same kind return DaemonError::FactoryAlreadyRegistered.

Valid in both Registering and Ready states; the runtime permits new kinds to appear at runtime. Only ShuttingDown rejects.

§Migration targeting

register_factory alone is not sufficient to accept inbound migrations — it registers the kind-to-closure mapping only on the SDK side. The core migration dispatcher looks up factories by origin_hash (the daemon’s identity), not by kind, because the migration wire protocol doesn’t carry a kind string; the target couldn’t pick the right factory from an inbound snapshot without an explicit binding.

To accept migrations for a specific daemon, the target must ALSO call one of:

  • Self::expect_migration (kind, origin_hash, config) — placeholder factory keyed by origin_hash; the envelope on the snapshot supplies the keypair at restore time. This is the common case.
  • Self::register_migration_target_identity (kind, identity, config) — pre-provisions the keypair as a fallback when the source migrates with transport_identity: false.

Or spawn the daemon locally first (via Self::spawn); spawn seeds both the SDK map and the core registry, so a daemon that migrated out and migrates back in on the same node is covered without extra calls.

Source

pub async fn start(&self) -> Result<(), DaemonError>

Promote to Ready. Idempotent — a second call on an already- Ready runtime is a no-op; a call on a ShuttingDown runtime returns DaemonError::ShuttingDown.

Wires the migration subprotocol (0x0500) handler into the mesh so inbound TakeSnapshot / SnapshotReady / etc. messages reach the orchestrator / source / target handlers owned by this runtime. Installing is idempotent w.r.t. multiple start calls — the ArcSwapOption on the mesh swaps the same handler in on each call.

Source

pub async fn shutdown(&self) -> Result<(), DaemonError>

Tear down the runtime. Unregisters every local daemon host, clears the factory registry, and transitions state to ShuttingDown. Subsequent calls on this runtime fail with DaemonError::ShuttingDown. A second shutdown is a no-op.

Source

pub fn simulate_not_ready(&self, flag: bool)

Test-only. Force the readiness predicate seen by the migration dispatcher to return false regardless of lifecycle state — simulates a target that’s still in Registering even after start() has run. Lets integration tests exercise the NotReady retry path without racing against runtime startup.

No effect on is_ready() or spawn / stop — those use the underlying state directly. Only the dispatcher’s readiness predicate is affected.

Source

pub fn is_ready(&self) -> bool

Readiness accessor for tests + operators. true iff the runtime has transitioned to Ready and has not yet begun shutting down.

Source

pub async fn spawn( &self, kind: &str, identity: Identity, config: DaemonHostConfig, ) -> Result<DaemonHandle, DaemonError>

Spawn a daemon of kind under the caller-provided Identity. The identity’s keypair seeds the daemon’s origin_hash + entity_id; the runtime registers both the live host and a kind-keyed factory in the core registry so a future migration target can reconstruct the daemon through the existing DaemonFactoryRegistry::construct path.

The returned DaemonHandle is clone-safe; dropping it does not stop the daemon. Call Self::stop explicitly.

Source

pub async fn spawn_with_daemon<F>( &self, identity: Identity, config: DaemonHostConfig, daemon: Box<dyn MeshDaemon>, kind_factory: F, ) -> Result<DaemonHandle, DaemonError>
where F: Fn() -> Box<dyn MeshDaemon> + Send + Sync + 'static,

Spawn a daemon with a caller-supplied MeshDaemon instance, bypassing the SDK-side kind-factory lookup.

Used by language-binding layers (currently: the NAPI compute module) that build daemon instances via cross-FFI dispatch — the factory closure for such a daemon can’t be a plain Fn() -> Box<dyn MeshDaemon> because constructing the daemon requires an awaitable call into the host language. The binding does the await itself, hands in the resulting Box<dyn MeshDaemon>, and this method does the rest of what Self::spawn does: register the (origin_hash → kind-factory) mirror in the core registry so future migrations can reconstruct the daemon, insert the host, and run the same shutdown-race fence.

kind_factory is the closure the core registry stores for migration-target reconstruction; it must be re-callable (migration targets call it when they restore the daemon on another node). Bindings typically build this by cloning the same TSFN used for the initial spawn.

Source

pub async fn spawn_from_snapshot_with_daemon<F>( &self, identity: Identity, snapshot: StateSnapshot, config: DaemonHostConfig, daemon: Box<dyn MeshDaemon>, kind_factory: F, ) -> Result<DaemonHandle, DaemonError>
where F: Fn() -> Box<dyn MeshDaemon> + Send + Sync + 'static,

Spawn a daemon from a caller-supplied instance and restore its state from snapshot, bypassing the SDK-side kind-factory lookup. Parallels Self::spawn_with_daemon for restore.

Used by language-binding layers whose daemons are built via cross-FFI dispatch — construction goes through the host language, then the binding hands the built Box<dyn MeshDaemon> (already wired to its TSFN bridge) plus the kind_factory closure used by the core registry for migration-target reconstruction.

Source

pub async fn spawn_from_snapshot( &self, kind: &str, identity: Identity, snapshot: StateSnapshot, config: DaemonHostConfig, ) -> Result<DaemonHandle, DaemonError>

Spawn a daemon of kind and restore its state from snapshot. The snapshot’s entity_id must match the caller’s Identity; mismatch returns DaemonError::SnapshotIdentityMismatch before any side effects land.

Source

pub async fn stop(&self, origin_hash: u64) -> Result<(), DaemonError>

Stop a daemon, removing it from the runtime’s registry. Valid while Ready and (idempotently) during ShuttingDownShuttingDown paths through here are a no-op because the shutdown sweep has already drained the registry.

Source

pub async fn snapshot( &self, origin_hash: u64, ) -> Result<Option<StateSnapshot>, DaemonError>

Take a snapshot of a running daemon by origin_hash. Returns Ok(None) when the daemon is stateless.

Source

pub fn deliver( &self, origin_hash: u64, event: &CausalEvent, ) -> Result<Vec<CausalEvent>, DaemonError>

Deliver one causal event to the daemon identified by origin_hash, returning the daemon’s outputs wrapped in the host’s causal chain.

Stage 1 convenience — Stage 2 adds mesh-dispatched delivery via the causal subprotocol, and this direct path becomes testing sugar rather than the primary ingress.

Source

pub fn daemon_count(&self) -> usize

Number of daemons currently registered.

Source

pub fn migration_phase(&self, origin_hash: u64) -> Option<MigrationPhase>

Current orchestrator-side migration phase for origin_hash, or None when no migration record exists (either never started here or already reached its terminal state and was removed). Useful for tests that assert the migration reached true completion (record gone via ActivateAck) rather than simply advancing to the Complete phase.

Source

pub fn subscriptions( &self, origin_hash: u64, ) -> Result<Vec<SubscriptionBinding>, DaemonError>

Snapshot the daemon’s subscription ledger — a cloned view of every (publisher, channel) pair the daemon has subscribed to via Self::subscribe_channel. Used by the migration target path to drive replay and by tests / operators to observe what a daemon is subscribed to.

Source

pub async fn subscribe_channel( &self, origin_hash: u64, publisher: u64, channel: ChannelName, token: Option<PermissionToken>, ) -> Result<(), DaemonError>

Subscribe a specific daemon to a channel on a remote publisher. Routes through the mesh’s membership subprotocol and records the subscription in the daemon’s ledger so a migration target can replay it after cutover. Users should use this method (rather than reaching through rt.mesh().inner().subscribe_channel_*) for daemon-owned subscriptions; otherwise the subscription travels with the node, not the daemon, and silently drops on migration.

Flow:

  1. Hit the publisher’s membership endpoint via Mesh::subscribe_channel_with_token (or the no-token variant).
  2. On success, record (publisher, channel) → SubscriptionBinding in the host’s ledger.
  3. On wire failure, no ledger mutation.

token is the caller-owned PermissionToken for token-gated channels; None for open channels.

Source

pub async fn unsubscribe_channel( &self, origin_hash: u64, publisher: u64, channel: ChannelName, ) -> Result<(), DaemonError>

Unsubscribe a specific daemon from a channel. Symmetric to Self::subscribe_channel: mesh wire call first, then ledger update.

Source

pub fn register_migration_target_identity( &self, kind: &str, identity: Identity, config: DaemonHostConfig, ) -> Result<(), DaemonError>

Pre-register a factory on the target node keyed by the daemon’s origin_hash, using the caller-supplied Identity as the fallback keypair.

Use this when:

  • The caller genuinely has the daemon’s keypair on hand (typical: test harnesses that share the same Identity between source and target runtimes).
  • Migration runs with transport_identity = false, so the snapshot carries no envelope and the target needs a matching keypair pre-provisioned.

For the common envelope-transport case where the target doesn’t know the daemon’s private key ahead of time, prefer Self::expect_migration — it registers a placeholder factory keyed only on origin_hash, and the envelope in the migration snapshot supplies the real keypair at restore time.

Source

pub fn expect_migration( &self, kind: &str, origin_hash: u64, config: DaemonHostConfig, ) -> Result<(), DaemonError>

Declare on the target that this node expects a migration for origin_hash of the given kind. Registers a placeholder factory in the core registry — no matching keypair required, because the migration snapshot’s IdentityEnvelope carries the real keypair and the dispatcher overrides the placeholder at restore time.

Fails cleanly if the source migrates without an envelope (e.g., MigrationOpts { transport_identity: false }) — the target’s factory has no keypair and the dispatcher emits IdentityTransportFailed. Use Self::register_migration_target_identity with a shared identity for the explicit public-identity-migration case.

Landing this method closes the seam documented in the envelope_overrides_target_placeholder_keypair test of Stage 5b of the identity-migration plan — targets can now pre-register for a migration by origin_hash alone.

Source

pub async fn start_migration( &self, origin_hash: u64, source_node: u64, target_node: u64, ) -> Result<MigrationHandle, DaemonError>

Start migrating a daemon from source_node to target_node. The orchestrator runs on this node regardless of who owns the daemon — call this on whichever node wants to drive the migration state machine.

Returns a MigrationHandle whose MigrationHandle::wait resolves when the migration reaches a terminal state (Complete on success, MigrationError on abort / failure).

For the common local-source case (source_node == mesh.node_id()), the snapshot is taken synchronously inside this call and SnapshotReady is shipped to the target. For a remote source, the orchestrator sends TakeSnapshot to the source and drives the rest of the state machine from inbound wire messages.

Source

pub async fn start_migration_with( &self, origin_hash: u64, source_node: u64, target_node: u64, opts: MigrationOpts, ) -> Result<MigrationHandle, DaemonError>

start_migration with caller-supplied options. Stage 6 of DAEMON_IDENTITY_MIGRATION_PLAN.md: lets the caller opt out of identity transport when the daemon doesn’t need to sign anything on the target.

Source

pub fn mesh(&self) -> &Arc<Mesh>

Underlying mesh. Exposed read-only so the caller can still reach the channel / subscribe / publish surface without reaching around the runtime.

Trait Implementations§

Source§

impl Clone for DaemonRuntime

Source§

fn clone(&self) -> DaemonRuntime

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DaemonRuntime

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more