Skip to content

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.

  1. Keeps only a node exactly matching the source version for planned maintenance, or the specified target version for rolling update.
  2. Keeps only a Serving Object Server on the same Mesh that isn't the source.
  3. Confirms stable type, factory, relocation policy, and state adapter compatibility.
  4. Confirms population capacity and reservation availability, and excludes the same maintenance wave as source.
  5. Keeps only a node whose RID and lifecycle generation match in the same descriptor snapshot and Core peer table, and whose peer is Admitted and Ready.
  6. 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.