pub struct LiminalConnectionNotifier { /* private fields */ }Expand description
Connection-keyed ConnectionNotifier that turns liminal’s in-band worker
registration into a first-class ConnectedWorkerRegistry membership.
This is the SERVER half of LSUB-L2: when a worker connects with a
WireWorkerRegistration (the SDK’s connect_with_registration), liminal’s
connection process invokes on_worker_registered
with the connection’s beamr pid and the worker’s declared
(namespaces, task_queue, node, activity_types). The notifier builds a
WorkerDelivery::Liminal over the connection and inserts it into the
registry — the SAME registry entry, selected the SAME way, as a gRPC worker —
retiring the LSUB-1 out-of-band active_connection_pids() + hard-coded
registration hack.
§Lifetime of the registration guard
ConnectedWorkerRegistry::register_delivery returns a
WorkerRegistration guard whose drop deregisters the worker. The notifier
OWNS that guard keyed by pid (Mutex<HashMap<u64, WorkerRegistration>>), so
the registration lives exactly as long as the connection: it is inserted on
register and removed (dropped) on
on_worker_unregistered, which liminal fires
on connection close.
§Construction-order cycle (notifier <-> supervisor)
LiminalWorkerDelivery needs a ConnectionSupervisor handle to push to
the worker’s connection, but the supervisor is itself constructed WITH this
notifier (ConnectionSupervisor::with_services_and_notifier) — a cycle. The
notifier therefore holds the supervisor behind a OnceLock, populated
IMMEDIATELY after the supervisor is built via Self::bind_supervisor. The
OnceLock is never read before it is set in correct wiring (a worker can only
register after the listener — built after the supervisor and after
bind_supervisor — accepts its connection); if it somehow were, registration
is REJECTED with a typed error rather than panicking, so there is no
production unwrap/expect and no second always-None code path.
Implementations§
Source§impl LiminalConnectionNotifier
impl LiminalConnectionNotifier
Sourcepub fn new(registry: ConnectedWorkerRegistry) -> Self
pub fn new(registry: ConnectedWorkerRegistry) -> Self
Build a notifier that registers connecting workers into registry.
The supervisor handle is bound separately via Self::bind_supervisor
immediately after the supervisor is constructed, resolving the
notifier <-> supervisor construction cycle (see the type docs).
Sourcepub fn with_admission(
self,
guard: NamespaceGuard,
auth_enabled: bool,
handle: Handle,
) -> Self
pub fn with_admission( self, guard: NamespaceGuard, auth_enabled: bool, handle: Handle, ) -> Self
Bind the namespace admission a liminal registration is judged by —
the same guard, mint-or-gate and placement admission the gRPC path
applies — and the Tokio runtime handle the synchronous
connection-process callback bridges onto to run it.
The handle is named, never captured from ambient context: the e2e
harnesses build this notifier in a sync start() BEFORE they build
their runtime, and a capture there was None, refusing every
registration on the very tests meant to exercise it while the
production path (built inside run_server) happened to work. A
listener commissioned outside any runtime has no handle to give and
refuses to be commissioned, at the call site, by name.
auth_enabled mirrors auth.enabled: the liminal protocol’s
registration frame carries an identity string and no credential, so
on a server that authenticates callers a liminal registration cannot
be scoped and is refused by name; on an auth-off server it is admitted
as the operator identity, exactly as a gRPC registration with no
metadata is.
Sourcepub fn with_contract_catalog(self, engine: Arc<Engine>) -> Self
pub fn with_contract_catalog(self, engine: Arc<Engine>) -> Self
Install the live package catalog used to refuse incompatible workers before liminal acknowledges or publishes their registration.
Sourcepub fn with_heartbeat_tracker(self, tracker: HeartbeatTracker) -> Self
pub fn with_heartbeat_tracker(self, tracker: HeartbeatTracker) -> Self
Install the shared per-task liveness tracker so a worker’s automatic
WorkerLivenessBeat publishes refresh the SAME in-flight entries the
bridge dispatcher tracks and the #176 sweeper expires.
MUST be wired on every boot that hosts the engine-seam bridge (the production composition does), or the sweeper would expire healthy liminal workers running activities longer than the heartbeat window. Without it (isolated tests that never bridge-dispatch) beats are consumed and dropped.
Sourcepub fn with_transcript_publisher(
self,
publisher: ActivityEventPublisher,
) -> Self
pub fn with_transcript_publisher( self, publisher: ActivityEventPublisher, ) -> Self
Install the transcript sequencer a worker’s observability publishes drain into (NOI-5b), capturing the CURRENT Tokio runtime handle to bridge the synchronous connection-process callback onto the async publish.
MUST be called from within a Tokio runtime (the server boot path is), so the
captured Handle can spawn the append+fan-out when a
worker publishes a transcript event over the reserved channel. Without this
builder the observability tap is a no-op (a plain, non-agent deployment).
§Panics
Panics if called outside a Tokio runtime — a construction-time wiring error in the server boot, never a runtime condition (the boot path always builds the notifier inside the server runtime).
Sourcepub fn with_intervention_capabilities(
self,
capabilities: InterventionCapabilities,
) -> Self
pub fn with_intervention_capabilities( self, capabilities: InterventionCapabilities, ) -> Self
Set the neutral intervention capability set every worker registering through this notifier advertises (NOI-6, item 4).
The composition root wires this from the harness’s advertised
AgentSession::capabilities() so a liminal-connected agent worker’s handle
carries the primitives its harness supports, which the intervention router
gates on. Without this builder the set is empty (observability-only), so a
plain activity worker advertises no controls. Pure builder addition, mirroring
the registry’s capability-carrying registration façade.
Sourcepub fn bind_supervisor(&self, supervisor: ConnectionSupervisor) -> bool
pub fn bind_supervisor(&self, supervisor: ConnectionSupervisor) -> bool
Bind the connection supervisor the notifier pushes through, immediately after it is constructed with this notifier.
Returns true when the supervisor was stored, false when it was already
bound (a second bind is a wiring bug and is ignored, never overwriting the
live handle). Call this exactly once, right after
ConnectionSupervisor::with_services_and_notifier.
Sourcepub fn liveness_targets(&self) -> Vec<LivenessTarget>
pub fn liveness_targets(&self) -> Vec<LivenessTarget>
Snapshot of every live liminal connection the dead-man switch must ping: its pid, the worker it registered, and the push leg to reach it.
Built from the SAME guard map that owns each connection’s registration, so a connection that has closed (its guard removed) is structurally absent from the snapshot and is never pinged. A poisoned guard map is recovered rather than skipped: silently returning no targets would turn the dead-man switch off exactly when the server is already unhealthy.
Trait Implementations§
Source§impl ConnectionNotifier for LiminalConnectionNotifier
impl ConnectionNotifier for LiminalConnectionNotifier
Source§fn on_worker_registered(
&self,
pid: u64,
registration: &WireWorkerRegistration,
) -> Result<(), LiminalServerError>
fn on_worker_registered( &self, pid: u64, registration: &WireWorkerRegistration, ) -> Result<(), LiminalServerError>
pid. Read moreSource§fn on_worker_unregistered(&self, pid: u64)
fn on_worker_unregistered(&self, pid: u64)
pid — which had a stored
registration — closes, so the application can release the association. Read moreSource§fn on_channel_publish(&self, pid: u64, channel: &str, payload: &[u8]) -> bool
fn on_channel_publish(&self, pid: u64, channel: &str, payload: &[u8]) -> bool
pid publishes to channel,
carrying the opaque envelope payload, BEFORE the normal channel fan-out. Read moreSource§fn on_pass_attached(&self, pid: u64, principal: &PassPrincipal)
fn on_pass_attached(&self, pid: u64, principal: &PassPrincipal)
Connect carrying a registry pass has
SUCCEEDED on the connection identified by pid — after the pass
verified, version negotiation passed, and the handshake was admitted —
carrying the principal the pass stamped: participant, public key, live
prefix, conversation scope, may_enroll. pid is the connection’s own
id, the same one every other hook on this trait is keyed by, so the
matching on_pass_detached can be paired. Read moreSource§fn on_pass_detached(&self, pid: u64, principal: &PassPrincipal)
fn on_pass_detached(&self, pid: u64, principal: &PassPrincipal)
on_pass_attached closes — for any reason: a
clean Disconnect, the peer ending the transport, a transport error, a
process crash, or supervisor shutdown — carrying the same pid and the
principal the attach carried. Best-effort and infallible like
on_worker_unregistered: it runs on the
close path where there is no peer to report to. The default does
nothing.Auto Trait Implementations§
impl !Freeze for LiminalConnectionNotifier
impl !RefUnwindSafe for LiminalConnectionNotifier
impl !UnwindSafe for LiminalConnectionNotifier
impl Send for LiminalConnectionNotifier
impl Sync for LiminalConnectionNotifier
impl Unpin for LiminalConnectionNotifier
impl UnsafeUnpin for LiminalConnectionNotifier
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoMaybeUndefined<T> for T
impl<T> IntoMaybeUndefined<T> for T
Source§fn into_maybe_undefined(self) -> MaybeUndefined<T>
fn into_maybe_undefined(self) -> MaybeUndefined<T>
Source§impl<T> IntoOption<T> for T
impl<T> IntoOption<T> for T
Source§fn into_option(self) -> Option<T>
fn into_option(self) -> Option<T>
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request