Node.js Location Operational Query And Observability Public Interface¶
This document only defines the public interface an application uses to query state and process events. Storage row change monitoring, runtime event publishing, serializer selection, and the handler call wrapper are framework-internal responsibilities.
1. Handler Filter¶
A filter receives a dedicated context containing only message
information and the public dispatch kind. Socket, endpoint, internal
owner kind, and the decoded message aren't exposed. AbortSignal is
delivered when the dispatch is cancelled.
export interface ZLinkMessageContext {
readonly meshName?: string;
readonly channelName?: string;
readonly packetName: string;
readonly contentType?: string;
readonly metadata: ZLinkMessageMetadata;
readonly correlationId?: string;
}
export enum ZLinkHandlerDispatchKind {
NodeDirectSend = 'nodeDirectSend',
NodeDirectRequest = 'nodeDirectRequest',
ChannelSend = 'channelSend',
ChannelRequest = 'channelRequest',
ClassicFanout = 'classicFanout'
}
export interface ZLinkHandlerFilterContext extends ZLinkMessageContext {
readonly dispatchKind: ZLinkHandlerDispatchKind;
}
export type ZLinkHandlerFilterNext = () => Promise<void>;
export interface ZLinkHandlerFilter {
invoke(
context: ZLinkHandlerFilterContext,
next: ZLinkHandlerFilterNext,
signal?: AbortSignal
): Promise<void>;
}
ChannelSend and ChannelRequest together represent RouteMesh and
ClientServer Channel. RouteMesh and Node direct provide MeshName.
ClientServer and classic fanout don't provide MeshName.
A filter calls next() at most once. A second call fails with
ZLinkFrameworkErrorKind.InvalidOperation and doesn't re-run the
handler. If next() isn't called on a request, a
ZLinkFrameworkErrorKind.Rejected reply is sent. A filter's return
value doesn't create or change the business reply.
ZLinkHandlerInvocation isn't the public contract. A filter only
applies to Node direct send/request, Channel send/request, and classic
fanout subscription handlers. It doesn't apply to Spot/Actor/Logical
Multicast/STREAM handlers.
2. Location Operational Query¶
export interface ZLinkLocationRuntimeQuery {
getStatus(signal?: AbortSignal): Promise<ZLinkLocationRuntimeStatus>;
listTopology(
filter: ZLinkLocationTopologyFilter,
page?: ZLinkPageRequest,
signal?: AbortSignal
): Promise<ZLinkLocationPage<ZLinkLocationTopologyEntry>>;
listServiceSummaries(
filter: ZLinkLocationServiceSummaryFilter,
page?: ZLinkPageRequest,
signal?: AbortSignal
): Promise<ZLinkLocationPage<ZLinkLocationServiceSummary>>;
findActorLocation(
actorId: ActorId,
signal?: AbortSignal
): Promise<ZLinkLocationObjectEntry | undefined>;
findSpotLocation(
spotId: SpotId,
signal?: AbortSignal
): Promise<ZLinkLocationObjectEntry | undefined>;
listObjectLocations(
filter: ZLinkLocationObjectFilter,
page?: ZLinkPageRequest,
signal?: AbortSignal
): Promise<ZLinkLocationPage<ZLinkLocationObjectEntry>>;
}
export type ZLinkLocationObjectState = 'creating' | 'ready' | 'unavailable';
export interface ZLinkLocationObjectEntry {
readonly globalId: string;
readonly objectGeneration: bigint;
readonly meshName: string;
readonly nodeRid: RoutingId;
readonly state: ZLinkLocationObjectState;
readonly stableType: string;
}
export interface ZLinkLocationObjectFilter {
readonly objectKind: 'actor' | 'user_spot' | 'instance_spot';
readonly stableType?: string;
readonly meshName?: string;
}
export interface ZLinkLocationTopologyFilter {
readonly meshName?: string;
readonly nodeRid?: RoutingId;
readonly state?: ZLinkLocationTopologyState;
}
export interface ZLinkLocationTopologyEntry {
readonly meshName: string;
readonly nodeRid: RoutingId;
readonly endpoint: string;
readonly draining: boolean;
readonly state: ZLinkLocationTopologyState;
readonly updatedAt: Date;
}
export interface ZLinkLocationServiceSummaryFilter {
readonly meshName?: string;
}
export interface ZLinkLocationServiceSummary {
readonly meshName: string;
readonly totalCount: number;
readonly readyCount: number;
readonly errorCount: number;
readonly stoppedCount: number;
readonly lastUpdatedAt: Date;
}
export interface ZLinkLocationReadiness {
isPeerReady(
meshName: string,
role: ZLinkLocationRole,
nodeRid?: RoutingId,
signal?: AbortSignal
): Promise<boolean>;
}
Spot and Actor location queries are public operations for operational tools.
A direct lookup returns undefined for Missing, a creating entry for Creating,
a ready entry for Ready, and an unavailable entry when the current owner is
unavailable after commit. Spot direct lookup treats User Spot and Instance Spot
under the same Spot-ID lookup contract. A list requires objectKind, and takes
stableType and meshName as optional filters. A page contains 1..1000 items,
its encoded size is at most 4 MiB, and its continuation token is an opaque value
issued by the query. A Store query failure rejects the whole operation with
ZLinkFrameworkErrorKind.Unavailable and does not return a partial page.
The result is not an application-message target list or a placement input.
Route storage row queries, storage keys, ZLinkLocationAutoConnectType, watch
store, and change stamps remain runtime-internal contracts.
3. Runtime Status And Structured Log¶
The application confirms current state using the immutable status and change stream this document's runtime interfaces return. Raw socket/Location event DTOs, event handlers, sinks, and monitoring source registration options aren't the public contract.
The reason state changed is recorded by the standard structured logger the application configured. Native socket events, Location storage row changes, and Spot timer failures aren't delivered as public callbacks.
4. Host Relocation And Termination Runtime¶
Object relocation and host termination each start with
ZLinkFrameworkRuntime's relocate(options) and shutdown(). The
RouteMesh topology runtime only provides status queries and doesn't
change host lifecycle.
export enum ZLinkFrameworkRuntimeState {
Preparing = 0,
Serving = 1,
Relocating = 2,
Relocated = 3,
Draining = 4,
Stopped = 5,
Error = 6
}
export enum ZLinkFrameworkRelocationOutcome {
Relocated = 0,
Blocked = 1
}
export enum ZLinkFrameworkRelocationMode {
PlannedMaintenance = 0,
RollingUpdate = 1
}
export enum ZLinkFrameworkRelocationReason {
None = 0,
TargetUnavailable = 1,
StoreUnavailable = 2,
RelocationDisabled = 3,
StateIncompatible = 4,
DeadlineExceeded = 5,
RelocationFailed = 6,
RuntimeNotReady = 7,
ManualTopologyUnsupported = 8,
ShutdownRequested = 9,
OperationInProgress = 10
}
export interface ZLinkFrameworkRelocationOptions {
readonly mode: ZLinkFrameworkRelocationMode;
readonly targetApplicationVersion?: bigint;
readonly deadlineMs?: number;
readonly signal?: AbortSignal;
}
export interface ZLinkFrameworkRelocationResult {
readonly mode: ZLinkFrameworkRelocationMode;
readonly effectiveTargetApplicationVersion: bigint;
readonly outcome: ZLinkFrameworkRelocationOutcome;
readonly reason: ZLinkFrameworkRelocationReason;
}
export enum ZLinkFrameworkTerminationOutcome {
Stopped = 0,
ForceStopped = 1
}
export enum ZLinkFrameworkTerminationReason {
None = 0,
DeadlineExceeded = 1,
TeardownFailed = 2
}
export interface ZLinkFrameworkTerminationResult {
readonly outcome: ZLinkFrameworkTerminationOutcome;
readonly reason: ZLinkFrameworkTerminationReason;
}
export interface ZLinkFrameworkLifecycleOptions {
readonly deadlineMs?: number;
readonly signal?: AbortSignal;
}
export interface ZLinkFrameworkRuntimeStatus {
readonly state: ZLinkFrameworkRuntimeState;
readonly isReady: boolean;
readonly acceptingWork: boolean;
readonly deadline?: Date;
readonly relocationResult?: ZLinkFrameworkRelocationResult;
readonly terminationResult?: ZLinkFrameworkTerminationResult;
readonly capacity: ZLinkHostCapacityStatus;
readonly sequence: bigint;
readonly observedAt: Date;
}
export interface ZLinkObservationLoss {
readonly coalescedCount: bigint;
readonly discardedTerminalCount: bigint;
}
export interface ZLinkObservedStatus<TStatus> {
readonly status: TStatus;
readonly loss: ZLinkObservationLoss;
}
export interface ZLinkCoreHwmStatus {
readonly configuredMemoryLimitBytes?: bigint;
readonly configuredBudgetBytes?: bigint;
readonly configuredProfile: ZLinkCoreHwmProfile;
readonly effectiveBudgetBytes: bigint;
readonly totalAppliedHwmBytes: bigint;
readonly coreQueueAccountedBytes: bigint;
readonly applicationAccountedBytes: bigint;
readonly currentAccountedBytes: bigint;
readonly provisionalAccountedBytes: bigint;
readonly peakAccountedBytes: bigint;
readonly completionCurrentAccountedBytes: bigint;
readonly completionPeakAccountedBytes: bigint;
readonly completionPendingMessageCount: bigint;
readonly totalMessagingAccountedBytes: bigint;
readonly monitorQueueAppliedHwmBytes: bigint;
readonly monitorQueueAccountedBytes: bigint;
readonly totalInstanceAppliedHwmBytes: bigint;
readonly totalInstanceAccountedBytes: bigint;
readonly blockedRatioPpm: bigint;
readonly activeDirectionalQueueCount: bigint;
readonly activeCompletionDirectionalQueueCount: bigint;
readonly activeSendQueueCount: bigint;
readonly activeReceiveQueueCount: bigint;
readonly outstandingApplicationLeaseCount: bigint;
readonly retiredQueueCount: bigint;
readonly deferredOriginCreditBytes: bigint;
}
export interface ZLinkApplicationJobQueueStatus {
readonly configuredProfile: ZLinkApplicationJobQueueProfile;
readonly configuredManualMax?: bigint;
readonly configuredPauseThresholdPercent: number;
readonly configuredResumeThresholdPercent: number;
readonly effectiveProcessorCount: bigint;
readonly effectiveMaxQueuedApplicationJobs: bigint;
readonly pausePermitCount: bigint;
readonly resumePermitCount: bigint;
readonly reservedSupplyPermits: bigint;
readonly queuedApplicationJobs: bigint;
readonly permitsInUse: bigint;
readonly peakPermitsInUse: bigint;
readonly pressureState: "running" | "paused";
readonly currentPauseDurationSeconds: number;
readonly capacityWaiters: bigint;
readonly capacityWaitCount: bigint;
readonly capacityWaitDurationSeconds: number;
}
export interface ZLinkHostCapacityStatus {
readonly measurementEpoch: bigint;
readonly coreHwm: ZLinkCoreHwmStatus;
readonly applicationJobQueue: ZLinkApplicationJobQueueStatus;
}
export interface ZLinkFrameworkRuntime {
readonly status: ZLinkFrameworkRuntimeStatus;
resetCapacityMetrics(): void;
diagnosticsLevel: ZLinkMessageFlowLogMode;
observe(signal?: AbortSignal): AsyncIterable<ZLinkObservedStatus<ZLinkFrameworkRuntimeStatus>>;
relocate(options: ZLinkFrameworkRelocationOptions): Promise<ZLinkFrameworkRelocationResult>;
shutdown(options?: ZLinkFrameworkLifecycleOptions): Promise<ZLinkFrameworkTerminationResult>;
}
In ZLinkCoreHwmStatus, applicationAccountedBytes, outstandingApplicationLeaseCount,
retiredQueueCount, and deferredOriginCreditBytes are ABI-reserved compatibility fields and
are always 0n since 0.13.1. The framework projects them unchanged and does not reinterpret them
as Application Job Queue pressure.
Reading diagnosticsLevel returns the process's current diagnostics level;
changing it applies the new level starting at later message-processing
boundaries. The change is atomic and does not wait for message processing or
retroactively change records already in the telemetry queue.
If relocate(options) succeeds, the runtime becomes Relocated state,
and process and infrastructure connections are kept. The caller can
confirm the result is Relocated and then call shutdown(), or, if
relocation isn't needed, only call shutdown(). Calling shutdown()
during Relocating only confirms the currently running atomic
relocation unit to a terminal state and aborts the rest of relocation.
At this point, the relocation waiter receives
Blocked/ShutdownRequested. signal only cancels that Promise's wait.
It doesn't affect an already-started shared relocation or shutdown
operation, or other waiters.
The caller can't omit the relocation mode. PlannedMaintenance is used
for a node check or reboot that keeps the same application version. If
targetApplicationVersion is specified in this mode, the Promise
rejects with TypeError before changing application admission. A
valid call's effectiveTargetApplicationVersion is the source host's
application version.
RollingUpdate requires targetApplicationVersion, which must be
greater than source version. If the value is missing or at most source
version, it's rejected with TypeError the same way. The framework
only uses a node exactly matching the specified version as a candidate,
and doesn't substitute an intermediate version or a different, higher
version.
Target candidates are narrowed in the following order.
- Keeps only a node exactly matching the source version for planned maintenance, or the specified target version for rolling update.
- Keeps only a
ServingObject Server on the same Mesh that isn't the source. - Confirms stable type, factory, relocation policy, and state adapter compatibility.
- Confirms population capacity and reservation availability, and excludes the same maintenance wave as source.
- Keeps only a node whose RID and lifecycle generation match in the
same descriptor snapshot and Core peer table, and whose peer is
AdmittedandReady. - Applies node-wide placement weight to the remaining candidates.
Since the version filter is applied before capability/capacity/weight,
it doesn't fall back to a different version. If there's no Ready target
satisfying the condition, it's Blocked/TargetUnavailable.
While the same shared relocation is running, a call with the same mode
and effective target version joins the existing operation and receives
the same terminal result. The first call's deadlineMs fixes the shared
operation deadline, and a later joining call doesn't change it. A call
whose mode or target version differs doesn't change the running
operation or queue — it returns Blocked/OperationInProgress. This
result records the rejected call's requested mode and effective target
version.
5. RouteMesh Runtime Status And Readiness¶
meshName specifies the RouteMesh to query. An unregistered name fails
with a typed route error instead of creating new state. isReady(...)
is only true when the host is Serving and that RouteMesh topology
is Ready.
export enum ZLinkTopologyState {
Starting = 0,
Ready = 1,
Degraded = 2,
Stopping = 3,
Stopped = 4,
Failed = 5
}
export enum ZLinkPeerState {
Connecting = 0,
Ready = 1,
Draining = 2,
NotConnected = 3,
NotRequired = 4
}
export enum ZLinkTopologyReason {
RuntimeNotReady = 0,
NoReadyPeer = 1,
NoReadyTarget = 2,
LocationUnavailable = 3,
CapacityExceeded = 4,
Draining = 5,
InternalFailure = 6
}
export interface ZLinkPeerStatus {
readonly nodeRid: RoutingId;
readonly state: ZLinkPeerState;
readonly unavailableReason?: ZLinkTopologyReason;
}
export interface ZLinkChannelStatus {
readonly channelName: string;
readonly isReady: boolean;
readonly readyTargetCount: number;
}
export interface ZLinkPlacementStatus {
readonly isAvailable: boolean;
readonly activeActorCount: number;
readonly activeSpotCount: number;
readonly unavailableReason?: ZLinkTopologyReason;
}
export interface ZLinkRouteMeshStatus {
readonly meshName: string;
readonly state: ZLinkTopologyState;
readonly isReady: boolean;
readonly readyPeerCount: number;
readonly channels: readonly ZLinkChannelStatus[];
readonly peers: readonly ZLinkPeerStatus[];
readonly placement: ZLinkPlacementStatus;
readonly sequence: bigint;
readonly observedAt: Date;
}
export interface ZLinkRouteMeshRuntime {
snapshot(meshName: string): ZLinkRouteMeshStatus;
observe(
meshName: string,
capacity?: number,
signal?: AbortSignal
): AsyncIterable<ZLinkObservedStatus<ZLinkRouteMeshStatus>>;
isReady(meshName: string): boolean;
}
placement.isAvailable is only true when both Actor/Spot capacity
and activation concurrency have room. Activation concurrency's current
value and limit aren't exposed as a separate field in status.
NotConnected is a state where the topology needs a connection but
there's no ready connection. NotRequired is a normal state where
neither Object Client has RouteMesh Channel Server membership so a
connection isn't needed. The same applies when only Channel Client
membership is registered. If either side has Channel Server membership,
including weight 0, absence of connection is NotConnected. Both
states are excluded from ready peer count, but NotRequired isn't
included in liveness/health failure aggregation.
6. ClientServer And Fanout Runtime Status¶
An endpoint on the same process also follows the same candidate selection and connection status contract as a remote endpoint. The observation stream delivers a complete status after a change, not an event holding only some fields.
export type ZLinkClientServerRole = 'client' | 'server' | 'clientAndServer';
export interface ZLinkClientServerTargetStatus {
readonly nodeRid: RoutingId;
readonly weight: number;
readonly state: ZLinkPeerState;
readonly unavailableReason?: ZLinkTopologyReason;
}
export interface ZLinkClientServerStatus {
readonly channelName: string;
readonly localRole: ZLinkClientServerRole;
readonly state: ZLinkTopologyState;
readonly isReady: boolean;
readonly readyTargetCount: number;
readonly targets: readonly ZLinkClientServerTargetStatus[];
readonly sequence: bigint;
readonly observedAt: Date;
}
export interface ZLinkClientServerRuntime {
snapshot(channelName: string): ZLinkClientServerStatus;
observe(
channelName: string,
capacity?: number,
signal?: AbortSignal
): AsyncIterable<ZLinkObservedStatus<ZLinkClientServerStatus>>;
isReady(channelName: string): boolean;
}
export interface ZLinkFanoutStatus {
readonly channelName: string;
readonly state: ZLinkTopologyState;
readonly isReady: boolean;
readonly readyPublisherCount: number;
readonly publishers: readonly ZLinkPeerStatus[];
readonly sequence: bigint;
readonly observedAt: Date;
}
export interface ZLinkFanoutRuntime {
snapshot(channelName: string): ZLinkFanoutStatus;
observe(
channelName: string,
capacity?: number,
signal?: AbortSignal
): AsyncIterable<ZLinkObservedStatus<ZLinkFanoutStatus>>;
}
Peer status only provides nodeRid, state, unavailableReason.
Lifecycle generation, descriptor source, connection intent, internal
admission/claim/drain state, and pending request count are only used by
the framework to judge connection and ownership. The application
doesn't receive these values.
All four observe(...) methods deliver ZLinkObservedStatus<TStatus>; loss
has type ZLinkObservationLoss. coalescedCount and discardedTerminalCount
have type bigint. Runtime monitoring §7.2 owns the delivery unit, loss-counter semantics, range
and saturation, and subscription lifetime contract. signal is the subscription
cancellation input.
7. Message Wrapper¶
export declare class ZLinkMessage<TValue = unknown> {
private constructor();
static from<T>(value: T, declaredType?: Type<T>): ZLinkMessage<T>;
static fromEncoded(payload: ZLinkEncodedPayload): ZLinkMessage;
decode<T>(type?: Type<T>): T;
toEncodedPayload(): ZLinkEncodedPayload;
isEncoded(): boolean;
}
TypeScript doesn't retain a call site's static type at runtime. For an ordinary class
instance, its constructor is used as the declared type. When a runtime subtype instance is
passed through a base-class declaration, the second declaredType argument specifies that
base-class constructor. A TypeScript interface has no runtime constructor, so representing
an interface contract requires a constructor token for an application-defined class
compatible with that interface. The codec selector receives this token instead of the
instance's runtime subtype.
The first decode(...) on a received message fixes either a value or a failure. Another
call with the same or a different type returns that first outcome and does not run the
decoder again. If the first call failed, the same failure is delivered again. Because a
TypeScript generic type is not retained at runtime, the caller is responsible for whether
the already-fixed value can be interpreted as another type.
Serializer registry selection and the default-serializer decision helper are kept internal to the runtime. The application registers a codec in the Framework configuration, and doesn't pass a per-message selector or registry.