@zlink-systems/framework 0.30.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/LICENSE +150 -68
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/contracts/Actors/ZLinkActorClient.d.ts +2 -1
  4. package/dist/contracts/Channels/Calls.d.ts +2 -1
  5. package/dist/contracts/Common/ZLinkMessageMetadata.js +11 -5
  6. package/dist/contracts/Handlers/Attributes.d.ts +5 -13
  7. package/dist/contracts/Handlers/Attributes.js +7 -11
  8. package/dist/contracts/Handlers/JsonContract.d.ts +2 -46
  9. package/dist/contracts/Handlers/JsonContract.js +4 -153
  10. package/dist/contracts/Spots/Contracts.d.ts +2 -1
  11. package/dist/nest-integration.d.ts +1 -1
  12. package/dist/nest-integration.js +2 -1
  13. package/dist/runtime/actors/actor-client.d.ts +1 -1
  14. package/dist/runtime/actors/actor-client.js +14 -12
  15. package/dist/runtime/actors/actor-handoff.d.ts +1 -1
  16. package/dist/runtime/actors/actor-handoff.js +7 -4
  17. package/dist/runtime/actors/actor-join-relocation.d.ts +1 -0
  18. package/dist/runtime/actors/actor-local-native-join.js +9 -12
  19. package/dist/runtime/actors/actor-remote-wire.js +15 -0
  20. package/dist/runtime/actors/actor-routed-join-transport.d.ts +6 -2
  21. package/dist/runtime/actors/actor-routed-json-request.d.ts +2 -0
  22. package/dist/runtime/actors/index.d.ts +2 -1
  23. package/dist/runtime/actors/index.js +18 -6
  24. package/dist/runtime/actors/spot-actor-dispatch.js +2 -2
  25. package/dist/runtime/admission.d.ts +1 -1
  26. package/dist/runtime/admission.js +15 -5
  27. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +6 -8
  28. package/dist/runtime/application-jobs/application-ingress-record-owner.js +39 -22
  29. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +2 -1
  30. package/dist/runtime/application-jobs/application-job-queue-scope.js +37 -22
  31. package/dist/runtime/application-jobs/contracts.d.ts +10 -1
  32. package/dist/runtime/backend/contracts/index.d.ts +18 -9
  33. package/dist/runtime/backend/mesh-completion-table.d.ts +9 -1
  34. package/dist/runtime/backend/mesh-completion-table.js +149 -49
  35. package/dist/runtime/backend/mesh-dispatch-pump.d.ts +3 -0
  36. package/dist/runtime/backend/mesh-dispatch-pump.js +32 -11
  37. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
  38. package/dist/runtime/backend/node/node-backend-adapter-support.js +16 -9
  39. package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
  40. package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +5 -2
  41. package/dist/runtime/backend/node/node-raw-binding-port.js +42 -22
  42. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +13 -5
  43. package/dist/runtime/backend/node/node-raw-mesh-backend.js +69 -51
  44. package/dist/runtime/backend/node/node-socket-backend-adapter.js +6 -6
  45. package/dist/runtime/backend/raw-binding-port.d.ts +2 -2
  46. package/dist/runtime/backend/runtime-message.d.ts +1 -0
  47. package/dist/runtime/backend/runtime-message.js +5 -0
  48. package/dist/runtime/backend/runtime-values.d.ts +1 -0
  49. package/dist/runtime/backend/runtime-values.js +2 -1
  50. package/dist/runtime/channels/channel-clients.d.ts +1 -0
  51. package/dist/runtime/channels/channel-clients.js +58 -21
  52. package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +3 -2
  53. package/dist/runtime/channels/channel-dispatch-pipeline.js +20 -11
  54. package/dist/runtime/channels/channel-dispatch-services.d.ts +1 -0
  55. package/dist/runtime/channels/channel-dispatch-services.js +45 -0
  56. package/dist/runtime/channels/channel-dispatchers.d.ts +1 -1
  57. package/dist/runtime/channels/channel-dispatchers.js +27 -26
  58. package/dist/runtime/channels/channel-envelope-inspection.d.ts +1 -2
  59. package/dist/runtime/channels/channel-envelope-inspection.js +1 -16
  60. package/dist/runtime/channels/channel-envelope.d.ts +1 -0
  61. package/dist/runtime/channels/channel-envelope.js +83 -8
  62. package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
  63. package/dist/runtime/channels/channel-outbound-operations.js +8 -8
  64. package/dist/runtime/channels/channel-runtime-manager.d.ts +6 -6
  65. package/dist/runtime/channels/channel-runtime-manager.js +12 -12
  66. package/dist/runtime/channels/channel-transports.d.ts +18 -15
  67. package/dist/runtime/channels/channel-transports.js +48 -31
  68. package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
  69. package/dist/runtime/channels/local-spot-route-transport.js +2 -1
  70. package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +2 -2
  71. package/dist/runtime/channels/router-socket-spot-route-transport.js +4 -4
  72. package/dist/runtime/channels/source-spot-router.d.ts +2 -2
  73. package/dist/runtime/channels/source-spot-router.js +4 -4
  74. package/dist/runtime/channels/spot-node-route-transport.d.ts +2 -2
  75. package/dist/runtime/channels/spot-node-route-transport.js +4 -4
  76. package/dist/runtime/channels/spot-route-bridge-transport.d.ts +2 -2
  77. package/dist/runtime/channels/spot-route-bridge-transport.js +4 -4
  78. package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +5 -4
  79. package/dist/runtime/channels/spot-route-dispatch-strategy.js +18 -18
  80. package/dist/runtime/execution/serial-execution-queue.d.ts +1 -0
  81. package/dist/runtime/execution/serial-execution-queue.js +1 -0
  82. package/dist/runtime/foundation/actor-join-recovery-codec.js +2 -2
  83. package/dist/runtime/foundation/durable-service-request.d.ts +14 -0
  84. package/dist/runtime/foundation/durable-service-request.js +98 -0
  85. package/dist/runtime/foundation/operation-identity.d.ts +1 -0
  86. package/dist/runtime/foundation/operation-identity.js +5 -1
  87. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +22 -5
  88. package/dist/runtime/foundation/raw-service-mesh-runtime.js +356 -232
  89. package/dist/runtime/foundation/service-relocation-object-owner.d.ts +1 -0
  90. package/dist/runtime/foundation/service-relocation-object-owner.js +1 -0
  91. package/dist/runtime/foundation/service-relocation-runtime.d.ts +2 -0
  92. package/dist/runtime/foundation/service-relocation-runtime.js +13 -1
  93. package/dist/runtime/foundation/service-runtime-contracts.d.ts +2 -0
  94. package/dist/runtime/foundation/service-stateful-runtime.d.ts +4 -4
  95. package/dist/runtime/foundation/service-stateful-runtime.js +44 -103
  96. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +0 -5
  97. package/dist/runtime/foundation/service-topology-registry.d.ts +12 -2
  98. package/dist/runtime/foundation/service-topology-registry.js +61 -27
  99. package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +3 -3
  100. package/dist/runtime/foundation/service-wire-m6a-codec.js +36 -38
  101. package/dist/runtime/handlers/handler-instance-scope.d.ts +8 -0
  102. package/dist/runtime/handlers/handler-instance-scope.js +54 -11
  103. package/dist/runtime/host/actor-runtime-options-factory.js +2 -2
  104. package/dist/runtime/host/application-job-queue.d.ts +19 -7
  105. package/dist/runtime/host/application-job-queue.js +150 -42
  106. package/dist/runtime/host/entry-actor-runtime.d.ts +1 -0
  107. package/dist/runtime/host/entry-actor-runtime.js +3 -0
  108. package/dist/runtime/host/index.d.ts +7 -1
  109. package/dist/runtime/host/index.js +142 -118
  110. package/dist/runtime/host/remote-bound-session-relay.js +2 -18
  111. package/dist/runtime/host/route-mesh-runtime.js +1 -1
  112. package/dist/runtime/host/service-relocation-control.d.ts +1 -0
  113. package/dist/runtime/host/service-relocation-control.js +10 -4
  114. package/dist/runtime/host/service-relocation-host-runtime.d.ts +6 -8
  115. package/dist/runtime/host/service-relocation-host-runtime.js +213 -241
  116. package/dist/runtime/host/spot-address-transport.js +3 -2
  117. package/dist/runtime/host/spot-runtime-options-factory.js +1 -0
  118. package/dist/runtime/locations/actor-location-claims.d.ts +2 -2
  119. package/dist/runtime/locations/actor-location-claims.js +34 -19
  120. package/dist/runtime/locations/lifecycle.d.ts +1 -2
  121. package/dist/runtime/locations/lifecycle.js +2 -5
  122. package/dist/runtime/locations/runtime.d.ts +1 -1
  123. package/dist/runtime/locations/runtime.js +24 -23
  124. package/dist/runtime/messaging/framework-json-v1.d.ts +4 -6
  125. package/dist/runtime/messaging/framework-json-v1.js +38 -294
  126. package/dist/runtime/messaging/packet-name.d.ts +1 -1
  127. package/dist/runtime/messaging/packet-name.js +8 -24
  128. package/dist/runtime/messaging/request-result.d.ts +2 -0
  129. package/dist/runtime/messaging/request-result.js +54 -0
  130. package/dist/runtime/messaging/submission-result.d.ts +1 -0
  131. package/dist/runtime/messaging/submission-result.js +5 -2
  132. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +15 -0
  133. package/dist/runtime/protocol/service_wire_codec.generated.js +144 -0
  134. package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +9 -0
  135. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +14 -1
  136. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +7 -2
  137. package/dist/runtime/spots/formal-remote-actor-admission-registry.js +33 -19
  138. package/dist/runtime/spots/index.d.ts +3 -0
  139. package/dist/runtime/spots/index.js +9 -2
  140. package/dist/runtime/spots/spot-activation.d.ts +1 -0
  141. package/dist/runtime/spots/spot-activation.js +17 -8
  142. package/dist/runtime/spots/spot-entry-activation.js +6 -2
  143. package/dist/runtime/spots/spot-handler-registry.d.ts +3 -1
  144. package/dist/runtime/spots/spot-handler-registry.js +18 -8
  145. package/dist/runtime/spots/spot-node-autoconnect.js +4 -3
  146. package/dist/runtime/spots/spot-outbound.d.ts +3 -0
  147. package/dist/runtime/spots/spot-outbound.js +24 -15
  148. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.d.ts +1 -0
  149. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +1 -1
  150. package/dist/runtime/spots/spot-runtime-ports.d.ts +1 -0
  151. package/dist/runtime/spots/spot-subscription-dispatch.js +0 -3
  152. package/dist/runtime/spots/spot-timer.js +1 -0
  153. package/dist/runtime/streams/stream-session-runtime.d.ts +1 -1
  154. package/dist/runtime/streams/stream-session-runtime.js +13 -27
  155. package/package.json +2 -2
  156. package/src/contracts/Actors/ZLinkActorClient.ts +2 -1
  157. package/src/contracts/Channels/Calls.ts +2 -1
  158. package/src/contracts/Common/ZLinkMessageMetadata.ts +13 -8
  159. package/src/contracts/Handlers/Attributes.ts +14 -21
  160. package/src/contracts/Handlers/JsonContract.ts +8 -216
  161. package/src/contracts/Spots/Contracts.ts +2 -1
  162. package/src/nest-integration.ts +1 -0
  163. package/src/runtime/actors/actor-client.ts +40 -16
  164. package/src/runtime/actors/actor-handoff.ts +9 -4
  165. package/src/runtime/actors/actor-join-relocation.ts +1 -0
  166. package/src/runtime/actors/actor-local-native-join.ts +17 -26
  167. package/src/runtime/actors/actor-remote-wire.ts +17 -0
  168. package/src/runtime/actors/actor-routed-join-transport.ts +14 -4
  169. package/src/runtime/actors/actor-routed-json-request.ts +10 -2
  170. package/src/runtime/actors/index.ts +19 -5
  171. package/src/runtime/actors/spot-actor-dispatch.ts +4 -2
  172. package/src/runtime/admission.ts +18 -5
  173. package/src/runtime/application-jobs/application-ingress-record-owner.ts +41 -25
  174. package/src/runtime/application-jobs/application-job-queue-scope.ts +51 -22
  175. package/src/runtime/application-jobs/contracts.ts +14 -1
  176. package/src/runtime/backend/contracts/index.ts +53 -14
  177. package/src/runtime/backend/mesh-completion-table.ts +156 -57
  178. package/src/runtime/backend/mesh-dispatch-pump.ts +40 -10
  179. package/src/runtime/backend/node/node-backend-adapter-support.ts +17 -8
  180. package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +12 -2
  181. package/src/runtime/backend/node/node-raw-binding-port.ts +57 -30
  182. package/src/runtime/backend/node/node-raw-mesh-backend.ts +128 -65
  183. package/src/runtime/backend/node/node-socket-backend-adapter.ts +21 -6
  184. package/src/runtime/backend/raw-binding-port.ts +7 -2
  185. package/src/runtime/backend/runtime-message.ts +6 -0
  186. package/src/runtime/backend/runtime-values.ts +1 -0
  187. package/src/runtime/channels/channel-clients.ts +90 -41
  188. package/src/runtime/channels/channel-dispatch-pipeline.ts +69 -34
  189. package/src/runtime/channels/channel-dispatch-services.ts +69 -1
  190. package/src/runtime/channels/channel-dispatchers.ts +29 -27
  191. package/src/runtime/channels/channel-envelope-inspection.ts +2 -16
  192. package/src/runtime/channels/channel-envelope.ts +133 -8
  193. package/src/runtime/channels/channel-outbound-operations.ts +22 -8
  194. package/src/runtime/channels/channel-runtime-manager.ts +29 -12
  195. package/src/runtime/channels/channel-transports.ts +128 -60
  196. package/src/runtime/channels/local-spot-route-transport.ts +3 -1
  197. package/src/runtime/channels/router-socket-spot-route-transport.ts +13 -4
  198. package/src/runtime/channels/source-spot-router.ts +8 -4
  199. package/src/runtime/channels/spot-node-route-transport.ts +8 -4
  200. package/src/runtime/channels/spot-route-bridge-transport.ts +6 -4
  201. package/src/runtime/channels/spot-route-dispatch-strategy.ts +47 -18
  202. package/src/runtime/execution/serial-execution-queue.ts +2 -0
  203. package/src/runtime/foundation/actor-join-recovery-codec.ts +2 -2
  204. package/src/runtime/foundation/durable-service-request.ts +137 -0
  205. package/src/runtime/foundation/operation-identity.ts +5 -1
  206. package/src/runtime/foundation/raw-service-mesh-runtime.ts +484 -293
  207. package/src/runtime/foundation/service-relocation-object-owner.ts +2 -0
  208. package/src/runtime/foundation/service-relocation-runtime.ts +18 -1
  209. package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
  210. package/src/runtime/foundation/service-stateful-runtime.ts +67 -125
  211. package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -5
  212. package/src/runtime/foundation/service-topology-registry.ts +91 -36
  213. package/src/runtime/foundation/service-wire-m6a-codec.ts +57 -38
  214. package/src/runtime/handlers/handler-instance-scope.ts +67 -16
  215. package/src/runtime/host/actor-runtime-options-factory.ts +13 -2
  216. package/src/runtime/host/application-job-queue.ts +175 -41
  217. package/src/runtime/host/entry-actor-runtime.ts +4 -0
  218. package/src/runtime/host/index.ts +201 -156
  219. package/src/runtime/host/remote-bound-session-relay.ts +2 -23
  220. package/src/runtime/host/route-mesh-runtime.ts +1 -1
  221. package/src/runtime/host/service-relocation-control.ts +11 -5
  222. package/src/runtime/host/service-relocation-host-runtime.ts +315 -291
  223. package/src/runtime/host/spot-address-transport.ts +4 -2
  224. package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
  225. package/src/runtime/locations/actor-location-claims.ts +52 -30
  226. package/src/runtime/locations/lifecycle.ts +8 -26
  227. package/src/runtime/locations/runtime.ts +28 -24
  228. package/src/runtime/messaging/framework-json-v1.ts +5 -325
  229. package/src/runtime/messaging/packet-name.ts +5 -25
  230. package/src/runtime/messaging/request-result.ts +58 -0
  231. package/src/runtime/messaging/submission-result.ts +5 -1
  232. package/src/runtime/protocol/service_wire_codec.generated.ts +131 -1
  233. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +4 -4
  234. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +43 -23
  235. package/src/runtime/spots/index.ts +14 -2
  236. package/src/runtime/spots/spot-activation.ts +28 -10
  237. package/src/runtime/spots/spot-entry-activation.ts +9 -2
  238. package/src/runtime/spots/spot-handler-registry.ts +41 -8
  239. package/src/runtime/spots/spot-node-autoconnect.ts +6 -2
  240. package/src/runtime/spots/spot-outbound.ts +36 -16
  241. package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +2 -1
  242. package/src/runtime/spots/spot-runtime-ports.ts +1 -0
  243. package/src/runtime/spots/spot-subscription-dispatch.ts +0 -3
  244. package/src/runtime/spots/spot-timer.ts +5 -1
  245. package/src/runtime/streams/stream-session-runtime.ts +12 -29
@@ -1,3 +1,4 @@
1
+ import { durableRequestCanReplay } from '../foundation/durable-service-request';
1
2
  import { UINT64_MAX, ZlinkStreamContentType } from '@zlink-systems/stream-wire';
2
3
  import { randomBytes } from 'node:crypto';
3
4
  import {
@@ -44,7 +45,7 @@ import {
44
45
  type ZLinkRemoteBoundSessionTarget
45
46
  } from '../actors/actor-runtime-state';
46
47
  import type { ZLinkBackendMeshNode, ZLinkMeshCompletionTable } from '../backend';
47
- import { RequestResult, SubmitResult } from '../backend/runtime-values';
48
+ import { RequestResult, SubmitResult, ZLinkBackendResultError } from '../backend/runtime-values';
48
49
  import type { ZLinkFrameworkRegistration } from '../configuration';
49
50
  import {
50
51
  type ZLinkRuntimeEventPublisher,
@@ -184,13 +185,13 @@ import {
184
185
  } from './relocation-state-adapter';
185
186
  import {
186
187
  decodeServiceRelocationControlRequest,
188
+ serviceRelocationControlCommand,
187
189
  decodeServiceRelocationControlResponse,
188
190
  encodeServiceRelocationControlRequest,
189
191
  encodeServiceRelocationControlResponse,
190
192
  type ZLinkServiceRelocationControlRequest,
191
193
  type ZLinkServiceRelocationControlResponse
192
194
  } from './service-relocation-control';
193
- const RELOCATION_ATTEMPT_TIMEOUT_MS = 250;
194
195
 
195
196
  const RELOCATION_AUTHORITY_RETRY_DELAY_MS = 25;
196
197
 
@@ -215,6 +216,7 @@ const RELOCATION_PARTICIPANT_STATE_LIMIT_BYTES = 64 * 1024 * 1024;
215
216
  const RELOCATION_TARGET_TOMBSTONE_LIMIT = 1024;
216
217
  const RELOCATION_OPERATION_RETENTION_MS = 5 * 60_000;
217
218
  const SERVICE_CONTROL_TERMINAL_CAPACITY = 4096;
219
+ const INITIAL_MEMBERSHIP_EPOCH = 1n;
218
220
 
219
221
  interface ZLinkHostRelocationOptions {
220
222
  readonly registration: ZLinkFrameworkRegistration;
@@ -222,6 +224,7 @@ interface ZLinkHostRelocationOptions {
222
224
  readonly locationStore: () => ZLinkLocationStore | undefined;
223
225
  readonly currentOwner: () => ZLinkLocationOwnerToken | undefined;
224
226
  readonly targetAdmissionSealed?: () => boolean;
227
+ readonly sourceQuiescenceChanged?: () => void;
225
228
  readonly liveDescriptors: (
226
229
  meshName: string,
227
230
  signal?: AbortSignal
@@ -280,6 +283,7 @@ interface LocalStage {
280
283
  finalize?: Promise<void>;
281
284
  /** The Restore's activation admission (MeshNode §5.1); held until target commit or abort. */
282
285
  readonly releaseActivation: () => void;
286
+ readonly releaseJoinAdmission: () => void;
283
287
  }
284
288
 
285
289
  interface TargetPayloadAssembly {
@@ -294,8 +298,8 @@ interface SourceCutoverWindow {
294
298
  readonly targetNodeRid: RoutingId;
295
299
  /** Boundary batch frames plus the cutover frame, retained until authority settlement. */
296
300
  frames?: readonly Buffer[];
297
- /** A failed submit or a lost target connection requires the whole batch again. */
298
- resendPending: boolean;
301
+ /** The ready route used to submit the retained batch. */
302
+ submittedRouteGeneration?: bigint;
299
303
  readonly cutoverSubmittedAtMs: number;
300
304
  followTimer?: ReturnType<typeof setTimeout>;
301
305
  windowClosed: boolean;
@@ -500,7 +504,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
500
504
  },
501
505
  input.relocationId,
502
506
  true,
503
- input.advertisedReceiveChunkLimitBytes
507
+ input.advertisedReceiveChunkLimitBytes,
508
+ input.deadlineAtMs
504
509
  );
505
510
  resolveReady();
506
511
  } catch (error) {
@@ -542,7 +547,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
542
547
  for (const window of this.sourceCutoverWindows.values()) {
543
548
  if (window.followTimer !== undefined) clearTimeout(window.followTimer);
544
549
  }
550
+ const hadSourceWindows = this.sourceCutoverWindows.size !== 0;
545
551
  this.sourceCutoverWindows.clear();
552
+ if (hadSourceWindows) this.options.sourceQuiescenceChanged?.();
546
553
  const stopped = new Error('Relocation runtime stopped.');
547
554
  stopped.name = 'AbortError';
548
555
  this.targetPrepareOperations.clear();
@@ -806,14 +813,32 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
806
813
  throw failure;
807
814
  }
808
815
 
809
- async tryHandleControl(
816
+ tryHandleControl(
810
817
  meshName: string,
811
818
  record: ReceiveRecord,
812
819
  signal?: AbortSignal
813
- ): Promise<boolean> {
820
+ ): boolean | Promise<boolean> {
814
821
  if (record.parts.length === 0) return false;
815
822
  const payload = record.parts[0]!.data();
816
- if (isServiceWireCommand(payload, M6bServiceWireCommand.replyRelay)) {
823
+ const command = serviceRelocationControlCommand(payload);
824
+ if (command === undefined) {
825
+ if (record.kind !== ReceiveKind.NodeSend) return false;
826
+ const sourceLeave = decodeRemoteActorSourceLeaveTerminal(payload);
827
+ if (sourceLeave === undefined) return false;
828
+ if (record.parts.length !== 1) return false;
829
+ return this.acceptActorJoinSourceLeave(sourceLeave);
830
+ }
831
+ return this.handleControl(meshName, record, payload, command, signal);
832
+ }
833
+
834
+ private async handleControl(
835
+ meshName: string,
836
+ record: ReceiveRecord,
837
+ payload: Buffer,
838
+ command: number,
839
+ signal?: AbortSignal
840
+ ): Promise<boolean> {
841
+ if (command === M6bServiceWireCommand.replyRelay) {
817
842
  await this.handleReplyRelay(
818
843
  meshName,
819
844
  decodeMaintenanceReplyRelay(record.parts.map((part) => part.data())),
@@ -822,25 +847,21 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
822
847
  );
823
848
  return true;
824
849
  }
825
- const sideband =
826
- record.parts.length === 1
827
- ? undefined
828
- : decodePrepareSideband(record.parts.slice(1).map((part) => part.data()));
829
- if (record.parts.length > 1 && sideband === undefined) return false;
850
+ if (record.parts.length !== 1) return false;
830
851
  if (record.kind === ReceiveKind.NodeSend) {
831
852
  const sourceLeave = decodeRemoteActorSourceLeaveTerminal(payload);
832
853
  if (sourceLeave !== undefined) {
833
854
  return this.acceptActorJoinSourceLeave(sourceLeave);
834
855
  }
835
856
  }
836
- if (isServiceWireCommand(payload, M6bServiceWireCommand.sessionRelocationSealed)) {
857
+ if (command === M6bServiceWireCommand.sessionRelocationSealed) {
837
858
  this.acceptSessionRelocationResponse(
838
859
  decodeSessionRelocationSealed(payload),
839
860
  record.sourceNodeRid
840
861
  );
841
862
  return true;
842
863
  }
843
- if (isServiceWireCommand(payload, M6bServiceWireCommand.sessionRelocationSeal)) {
864
+ if (command === M6bServiceWireCommand.sessionRelocationSeal) {
844
865
  const request = decodeSessionRelocationSeal(payload);
845
866
  const response = await this.handleSessionRelocationSeal(
846
867
  meshName,
@@ -855,12 +876,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
855
876
  );
856
877
  return true;
857
878
  }
858
- if (isServiceWireCommand(payload, M6bServiceWireCommand.sessionRelocationRoute)) {
879
+ if (command === M6bServiceWireCommand.sessionRelocationRoute) {
859
880
  const request = decodeSessionRelocationRoute(payload);
860
881
  await this.handleSessionRelocationRoute(meshName, request, record.sourceNodeRid, signal);
861
882
  return true;
862
883
  }
863
- if (isServiceWireCommand(payload, M6bServiceWireCommand.replyRelayAck)) {
884
+ if (command === M6bServiceWireCommand.replyRelayAck) {
864
885
  await this.acceptReplyRelayAck(
865
886
  meshName,
866
887
  decodeMaintenanceReplyRelayAck(payload),
@@ -871,10 +892,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
871
892
  }
872
893
  let request = decodeServiceRelocationControlRequest(payload);
873
894
  if (request === undefined) return false;
874
- if (sideband !== undefined) {
875
- if (request.kind !== 'prepare') return false;
876
- request = { ...request, nodeInternalBoundSessions: sideband };
877
- }
878
895
  // Ready(30) and Failed(53) are replies to Prepare(40), never independent
879
896
  // inbound controls. The source consumes them on Core's request reply leg.
880
897
  if (request.kind === 'ready' || request.kind === 'failed') return false;
@@ -1473,7 +1490,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
1473
1490
  const status = this.requireMeshNode(meshName).status();
1474
1491
  const owner = this.options.currentOwner();
1475
1492
  if (
1476
- String(status.routingId) !== session.sessionOwnerNodeRid ||
1493
+ !routingIdsEqual(status.routingId, session.sessionOwnerNodeRid) ||
1477
1494
  status.lifecycleGeneration !== session.sessionOwnerNodeGeneration ||
1478
1495
  owner?.ownerId !== session.sessionOwnerId ||
1479
1496
  owner.leaseGeneration !== session.sessionOwnerLeaseGeneration
@@ -1833,7 +1850,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
1833
1850
  aggregateIdOverride?: string,
1834
1851
  retainSourceForActorJoin = false,
1835
1852
  /** Target's advertised relocation state chunk cap (Actor Join only; spec 15 §4.2). */
1836
- advertisedReceiveChunkLimitBytes?: number
1853
+ advertisedReceiveChunkLimitBytes?: number,
1854
+ deadlineAtMs?: number
1837
1855
  ): Promise<void> {
1838
1856
  if (isRelocationDebugEnabled()) {
1839
1857
  relocationDebug('standalone_actor.begin', { actorId: state.actorId });
@@ -1922,7 +1940,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
1922
1940
  new Map([[measuredUnit.authorityKey, authority]]),
1923
1941
  signal,
1924
1942
  sessions,
1925
- advertisedReceiveChunkLimitBytes
1943
+ advertisedReceiveChunkLimitBytes,
1944
+ deadlineAtMs
1926
1945
  );
1927
1946
  } catch (error) {
1928
1947
  outcome = 'failed';
@@ -2128,14 +2147,37 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2128
2147
  }
2129
2148
  const prepared = standalone ? ownSession : sessions.find((value) => value.state === state);
2130
2149
  return {
2131
- boundSessionState: encodeActorSession(prepared?.prepared.target),
2150
+ boundSessionState: Buffer.alloc(0),
2151
+ ...(prepared?.prepared.target?.serviceWireRelocation === undefined
2152
+ ? {}
2153
+ : {
2154
+ sessionBinding: {
2155
+ participantId: 1n,
2156
+ sessionOwnerNodeRid: Buffer.from(
2157
+ encodeRoutingIdStorageHex(prepared.prepared.target.sessionNodeRid!),
2158
+ 'hex'
2159
+ ),
2160
+ sessionOwnerNodeGeneration:
2161
+ prepared.prepared.target.serviceWireRelocation.session
2162
+ .sessionOwnerNodeGeneration,
2163
+ sessionOwnerId:
2164
+ prepared.prepared.target.serviceWireRelocation.session.sessionOwnerId,
2165
+ sessionOwnerLeaseGeneration:
2166
+ prepared.prepared.target.serviceWireRelocation.session
2167
+ .sessionOwnerLeaseGeneration,
2168
+ sessionRid: Buffer.from(
2169
+ encodeRoutingIdStorageHex(prepared.prepared.target.sessionRid!),
2170
+ 'hex'
2171
+ ),
2172
+ bindingGeneration:
2173
+ prepared.prepared.target.serviceWireRelocation.session.bindingGeneration
2174
+ }
2175
+ }),
2132
2176
  queuedMessages: encodeHandoffQueuedMessages(prepared?.prepared.handoffBacklog ?? []),
2133
2177
  timers: []
2134
2178
  };
2135
2179
  },
2136
- // The canonical participant body (discriminator 1) is raw application
2137
- // state. The sealed bound-session target remains in sealed work and is
2138
- // transported only by Prepare's NODE-INTERNAL sideband frames.
2180
+ // The sealed Session identity is part of the canonical participant vector.
2139
2181
  captureApplicationState: (signal) =>
2140
2182
  this.captureApplication(registration.relocation, state.actor!, signal),
2141
2183
  commitSeal: async () => {
@@ -2229,7 +2271,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2229
2271
  signal?: AbortSignal,
2230
2272
  sessions: readonly SourceActorSession[] = [],
2231
2273
  /** Target's advertised relocation state chunk cap (Actor Join only; spec 15 §4.2). */
2232
- advertisedReceiveChunkLimitBytes?: number
2274
+ advertisedReceiveChunkLimitBytes?: number,
2275
+ controlDeadlineAtMs = performance.now() + RELOCATION_OPERATION_TIMEOUT_MS
2233
2276
  ): Promise<void> {
2234
2277
  if (isRelocationDebugEnabled()) {
2235
2278
  relocationDebug('coordinator.begin', {
@@ -2238,10 +2281,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2238
2281
  });
2239
2282
  }
2240
2283
  const localStatus = this.requireMeshNode(meshName).status();
2241
- const controlDeadlineAtMs =
2242
- signal === undefined
2243
- ? performance.now() + RELOCATION_OPERATION_TIMEOUT_MS
2244
- : Number.MAX_SAFE_INTEGER;
2245
2284
  const coordinator = {
2246
2285
  ownerId: primary.ownerId,
2247
2286
  leaseGeneration: primary.ownerLeaseGeneration,
@@ -2326,29 +2365,31 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2326
2365
  // queue and the assembly on the target), then stream the payload chunks
2327
2366
  // on the same ordered connection. The Ready reply arrives only after
2328
2367
  // the target assembled, verified, and restored the payload.
2329
- const readyOperation = this.sendControl(
2368
+ const ready = await this.sendControl(
2330
2369
  meshName,
2331
2370
  target.rid,
2332
2371
  prepare,
2333
2372
  signal,
2334
2373
  controlDeadlineAtMs,
2335
- encodePrepareSideband(captured.envelope)
2336
- );
2337
- try {
2338
- if (isRelocationDebugEnabled()) {
2339
- relocationDebug('coordinator.state_transfer_begin', {
2340
- aggregateId: captured.envelope.aggregateId
2341
- });
2374
+ async (attemptSignal) => {
2375
+ if (isRelocationDebugEnabled())
2376
+ relocationDebug('coordinator.state_transfer_begin', {
2377
+ aggregateId: captured.envelope.aggregateId
2378
+ });
2379
+ await this.sendRelocationStateChunks(
2380
+ meshName,
2381
+ target,
2382
+ prepare,
2383
+ encoded,
2384
+ plan,
2385
+ attemptSignal
2386
+ );
2387
+ if (isRelocationDebugEnabled())
2388
+ relocationDebug('coordinator.await_ready', {
2389
+ aggregateId: captured.envelope.aggregateId
2390
+ });
2342
2391
  }
2343
- await this.sendRelocationStateChunks(meshName, target, prepare, encoded, plan, signal);
2344
- } catch (chunkError) {
2345
- readyOperation.catch(() => undefined);
2346
- throw chunkError;
2347
- }
2348
- if (isRelocationDebugEnabled()) {
2349
- relocationDebug('coordinator.await_ready', { aggregateId: captured.envelope.aggregateId });
2350
- }
2351
- const ready = await readyOperation;
2392
+ );
2352
2393
  if (isRelocationDebugEnabled()) {
2353
2394
  relocationDebug('coordinator.ready_received', {
2354
2395
  aggregateId: captured.envelope.aggregateId
@@ -2413,14 +2454,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2413
2454
  // The cutover submit result is not the authority result. The memory
2414
2455
  // copy of the boundary batch and cutover stays until the authority
2415
2456
  // settlement below (spec 28 §4.4).
2416
- let submitFailure: unknown;
2457
+ const submittedRouteGeneration = this.requireMeshNode(meshName).observePeerRoute?.(
2458
+ target.rid
2459
+ ).generation;
2417
2460
  try {
2418
2461
  for (const record of boundaryBatch) {
2419
2462
  await this.submitControlFrame(meshName, target.rid, record.frame, 'relay');
2420
2463
  }
2421
2464
  await this.submitControlFrame(meshName, target.rid, cutoverFrame, 'cutover');
2422
2465
  } catch (error) {
2423
- submitFailure = error;
2466
+ if (!durableRequestCanReplay(error)) throw error;
2424
2467
  console.warn('[zlink.runtime.relocation.cutover_submit_failed]', relayAuthorityId, error);
2425
2468
  }
2426
2469
  const window = this.beginSourceCutoverWindow(
@@ -2428,7 +2471,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2428
2471
  target.rid,
2429
2472
  relayAuthorityId,
2430
2473
  [...boundaryBatch.map((value) => value.frame), cutoverFrame],
2431
- submitFailure !== undefined,
2474
+ submittedRouteGeneration,
2432
2475
  limits
2433
2476
  );
2434
2477
  try {
@@ -2461,7 +2504,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2461
2504
  // pending request without a terminal gets one Unavailable.
2462
2505
  const unavailable = createInternalFrameworkException(
2463
2506
  ZLinkFrameworkInternalErrorKind.RouteNotConnected,
2464
- `Relocation '${relayAuthorityId}' source owner lease ended before its authority settled.`,
2507
+ `Relocation '${relayAuthorityId}' source authority ended (source owner lease expired, authority Missing, or owner changed).`,
2465
2508
  true
2466
2509
  );
2467
2510
  await captured.discardSource(unavailable);
@@ -2518,6 +2561,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2518
2561
  signal
2519
2562
  );
2520
2563
  try {
2564
+ signal?.throwIfAborted();
2521
2565
  await this.sendControlOneWay(meshName, target.rid, {
2522
2566
  kind: 'state',
2523
2567
  relocation: identity.relocation,
@@ -2597,7 +2641,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2597
2641
  targetNodeRid: RoutingId,
2598
2642
  relocationKey: string,
2599
2643
  frames: readonly Buffer[],
2600
- initialSubmitFailed: boolean,
2644
+ submittedRouteGeneration: bigint | undefined,
2601
2645
  limits: ReturnType<ZLinkHostServiceRelocationRuntime['relocationLimits']>
2602
2646
  ): SourceCutoverWindow {
2603
2647
  const submittedAt = performance.now();
@@ -2605,12 +2649,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2605
2649
  meshName,
2606
2650
  targetNodeRid,
2607
2651
  frames,
2608
- resendPending: initialSubmitFailed,
2652
+ submittedRouteGeneration,
2609
2653
  cutoverSubmittedAtMs: submittedAt,
2610
2654
  windowClosed: false,
2611
2655
  followExpired: false
2612
2656
  };
2657
+ const wasQuiescent = this.isSafeToShutdown();
2613
2658
  this.sourceCutoverWindows.set(relocationKey, window);
2659
+ if (wasQuiescent) this.options.sourceQuiescenceChanged?.();
2614
2660
  window.followTimer = setTimeout(() => {
2615
2661
  window.followExpired = true;
2616
2662
  // S1→S4 route convergence: the span the source must keep its Message
@@ -2640,27 +2686,17 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2640
2686
  this.sourceCutoverWindows.get(relocationKey) === window
2641
2687
  ) {
2642
2688
  this.sourceCutoverWindows.delete(relocationKey);
2689
+ if (this.isSafeToShutdown()) this.options.sourceQuiescenceChanged?.();
2643
2690
  }
2644
2691
  }
2645
2692
 
2646
- /**
2647
- * Sends the whole boundary batch and cutover again after a failed submit
2648
- * or an observed loss of the target connection (spec 28 §4.4). The target
2649
- * replaces its partial span with the whole batch.
2650
- */
2693
+ /** Relocation flow §4.4: replay the whole batch on a different ready route. */
2651
2694
  private async resendSourceCutover(window: SourceCutoverWindow): Promise<void> {
2652
2695
  const frames = window.frames;
2653
2696
  if (frames === undefined) return;
2654
- const peers = this.requireMeshNode(window.meshName).peers();
2655
- const peer = peers.find(
2656
- (value) =>
2657
- value.routingId !== null && String(value.routingId) === String(window.targetNodeRid)
2658
- );
2659
- if (peer !== undefined && peer.state !== MeshPeerRuntimeState.Serving) {
2660
- window.resendPending = true;
2661
- return;
2662
- }
2663
- if (!window.resendPending) return;
2697
+ const route = this.requireMeshNode(window.meshName).observePeerRoute?.(window.targetNodeRid);
2698
+ if (route?.ready !== true || route.generation === window.submittedRouteGeneration) return;
2699
+ window.submittedRouteGeneration = route.generation;
2664
2700
  try {
2665
2701
  for (const frame of frames) {
2666
2702
  await this.submitControlFrame(
@@ -2670,9 +2706,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2670
2706
  'cutover retransmission'
2671
2707
  );
2672
2708
  }
2673
- window.resendPending = false;
2674
- } catch {
2675
- // The next settlement round retries the whole batch.
2709
+ } catch (error) {
2710
+ if (!durableRequestCanReplay(error)) throw error;
2676
2711
  }
2677
2712
  }
2678
2713
 
@@ -2684,8 +2719,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2684
2719
  * excludes a late target commit. `target`: the
2685
2720
  * target committed. `source`: Preserve fenced the target out while the
2686
2721
  * source owner lease was valid. `lost`: the source owner lease ended first
2687
- * or another authority owns the object. An uncertain Store or lease result
2688
- * keeps the work retained and repeats the same decision.
2722
+ * or authority is Missing or another authority owns the object. An uncertain
2723
+ * Store or lease result keeps the work retained and repeats the same decision.
2689
2724
  */
2690
2725
  private async settleSourceAuthority(
2691
2726
  envelope: ServiceRelocationEnvelope,
@@ -2871,17 +2906,24 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2871
2906
  throw new ServiceRelocationDataLostError(String(message));
2872
2907
  }
2873
2908
  const envelope = decodeServiceRelocationEnvelope(payload, request.targetAttemptGeneration);
2874
- const inventoryEnvelope = attachPrepareBoundSessions(
2875
- await this.resolveTargetParticipantInventory(request, envelope, signal),
2876
- request.nodeInternalBoundSessions
2877
- );
2878
- validatePrepareEnvelope(request, inventoryEnvelope);
2879
- const releaseActivation =
2880
- (await this.options.activationAdmission?.acquire(meshName, signal)) ?? (() => undefined);
2909
+ const recovery = canonicalActorJoinRecovery(envelope.participants);
2910
+ const releaseJoinAdmission =
2911
+ recovery === undefined
2912
+ ? () => undefined
2913
+ : this.options.spotManager()!.beginActorJoinRelocation(recovery.request.handoffId);
2881
2914
  let materialized:
2882
2915
  (Pick<LocalStage, 'owner' | 'staging'> & { readonly target: LocalTargetPort }) | undefined;
2883
2916
  let reservation: TargetRelocationReservation | undefined;
2917
+ let releaseActivation: () => void = () => undefined;
2884
2918
  try {
2919
+ const inventoryEnvelope = attachCanonicalBoundSessions(
2920
+ await this.resolveTargetParticipantInventory(meshName, request, envelope, signal),
2921
+ request,
2922
+ meshName
2923
+ );
2924
+ validatePrepareEnvelope(request, inventoryEnvelope);
2925
+ releaseActivation =
2926
+ (await this.options.activationAdmission?.acquire(meshName, signal)) ?? (() => undefined);
2885
2927
  // Deferred Join completion publishes into the source authority's
2886
2928
  // canonical relocation slot. Do that while the row is still CAS-able;
2887
2929
  // the aggregate marker installed by reserveTargetForPrepare deliberately
@@ -2908,10 +2950,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2908
2950
  lane: Promise.resolve(),
2909
2951
  cutoverReceived: false,
2910
2952
  boundaryRelay: [],
2911
- releaseActivation
2953
+ releaseActivation,
2954
+ releaseJoinAdmission
2912
2955
  };
2913
2956
  this.targetStages.set(stagingId, stage);
2914
2957
  } catch (error) {
2958
+ releaseJoinAdmission();
2915
2959
  releaseActivation();
2916
2960
  if (reservation !== undefined) {
2917
2961
  await this.abortTargetReservation(reservation, signal).catch(() => undefined);
@@ -3134,6 +3178,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3134
3178
  throw new ServiceRelocationPostCommitError(authority, stage.staging, error);
3135
3179
  }
3136
3180
  throw error;
3181
+ } finally {
3182
+ stage.releaseJoinAdmission();
3137
3183
  }
3138
3184
  }
3139
3185
 
@@ -3154,7 +3200,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3154
3200
  target.generation !== request.object.objectGeneration ||
3155
3201
  target.targetNodeRid !== preparedTarget.nodeRid ||
3156
3202
  target.targetNodeGeneration !== preparedTarget.nodeGeneration ||
3157
- target.authorityOwnerGeneration !== request.object.expectedAuthorityOwnerGeneration + 1n ||
3203
+ target.authorityOwnerGeneration !== request.object.expectedAuthorityOwnerGeneration ||
3158
3204
  target.ownerLeaseGeneration !== preparedTarget.ownerLeaseGeneration
3159
3205
  ) {
3160
3206
  throw new Error('Relocation data does not target the exact temporary Actor queue.');
@@ -3293,7 +3339,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3293
3339
  ): Promise<void> {
3294
3340
  const submitted = await this.sendInfrastructureControl(meshName, targetNodeRid, frame);
3295
3341
  if (submitted !== SubmitResult.Ok) {
3296
- throw new Error(`Relocation ${kind} was not accepted by RouteMesh.`);
3342
+ throw new ZLinkBackendResultError('submit', submitted, undefined, {
3343
+ cause: new Error(`Relocation ${kind} was not accepted by RouteMesh.`)
3344
+ });
3297
3345
  }
3298
3346
  }
3299
3347
 
@@ -3611,70 +3659,39 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3611
3659
  request: ZLinkServiceRelocationControlRequest,
3612
3660
  signal?: AbortSignal,
3613
3661
  deadlineAtMs: number = performance.now() + RELOCATION_OPERATION_TIMEOUT_MS,
3614
- sideband: readonly Uint8Array[] = []
3662
+ onSubmitted?: (signal: AbortSignal) => Promise<void>
3615
3663
  ): Promise<ReturnType<typeof decodeServiceRelocationControlResponse>> {
3616
3664
  const key = controlAckKey(request);
3617
- const frames =
3618
- sideband.length === 0
3619
- ? [encodeServiceRelocationControlRequest(request)]
3620
- : [encodeServiceRelocationControlRequest(request), ...sideband];
3621
- let lastRequestError: unknown;
3622
- for (;;) {
3623
- signal?.throwIfAborted();
3624
- const remainingMs = deadlineAtMs - performance.now();
3625
- if (remainingMs <= 0) {
3626
- throw createInternalFrameworkException(
3627
- ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
3628
- `Relocation control ACK '${key}' timed out.`,
3629
- true,
3630
- lastRequestError
3631
- );
3632
- }
3633
- const attemptTimeoutMs = Math.max(
3634
- 1,
3635
- Math.min(RELOCATION_ATTEMPT_TIMEOUT_MS, Math.ceil(remainingMs))
3665
+ const frames = [encodeServiceRelocationControlRequest(request)];
3666
+ signal?.throwIfAborted();
3667
+ const remainingMs = Math.ceil(deadlineAtMs - performance.now());
3668
+ if (remainingMs <= 0) {
3669
+ throw createInternalFrameworkException(
3670
+ ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
3671
+ `Relocation control ACK '${key}' timed out.`,
3672
+ true
3636
3673
  );
3637
- const startedAtMs = performance.now();
3638
- try {
3639
- const replyFrames = await this.requestInfrastructureControl(
3640
- meshName,
3641
- targetNodeRid,
3642
- frames,
3643
- attemptTimeoutMs
3644
- );
3645
- if (replyFrames.length !== 1) {
3646
- throw new ServiceWireProtocolError(
3647
- `Relocation control '${key}' reply must contain exactly one frame.`
3648
- );
3649
- }
3650
- const response = decodeServiceRelocationControlResponse(Buffer.from(replyFrames[0]!));
3651
- if (response.kind === 'failed') {
3652
- validateControlFailureResponse(request, response);
3653
- throw relocationFailureException(response);
3654
- }
3655
- validateControlResponse(request, response);
3656
- return response;
3657
- } catch (error) {
3658
- if (error instanceof ServiceWireProtocolError || error instanceof ZLinkFrameworkException) {
3659
- throw error;
3660
- }
3661
- lastRequestError = error;
3662
- if (isRelocationDebugEnabled()) {
3663
- relocationDebug('control.request_retry', {
3664
- key,
3665
- kind: request.kind,
3666
- targetRid: String(targetNodeRid)
3667
- });
3668
- }
3669
- const retryDelayMs = Math.min(
3670
- Math.max(0, attemptTimeoutMs - (performance.now() - startedAtMs)),
3671
- Math.max(0, deadlineAtMs - performance.now())
3672
- );
3673
- if (retryDelayMs > 0) {
3674
- await waitForRelocationRetry(retryDelayMs, signal);
3675
- }
3676
- }
3677
3674
  }
3675
+ const replyFrames = await this.requestInfrastructureControl(
3676
+ meshName,
3677
+ targetNodeRid,
3678
+ frames,
3679
+ remainingMs,
3680
+ signal,
3681
+ onSubmitted
3682
+ );
3683
+ if (replyFrames.length !== 1) {
3684
+ throw new ServiceWireProtocolError(
3685
+ `Relocation control '${key}' reply must contain exactly one frame.`
3686
+ );
3687
+ }
3688
+ const response = decodeServiceRelocationControlResponse(Buffer.from(replyFrames[0]!));
3689
+ if (response.kind === 'failed') {
3690
+ validateControlFailureResponse(request, response);
3691
+ throw relocationFailureException(response);
3692
+ }
3693
+ validateControlResponse(request, response);
3694
+ return response;
3678
3695
  }
3679
3696
 
3680
3697
  private async selectTarget(
@@ -3761,7 +3778,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3761
3778
  const publication = relocationPublication(prepare, envelope);
3762
3779
  const participants = envelope.participants.map((participant) => {
3763
3780
  const expected = authorities.get(participant.key)!;
3764
- const membership = actorMembershipTarget(envelope, participant.key);
3781
+ const membership = actorMembershipTarget(
3782
+ envelope.memberships.find((value) => value.actorKey === participant.key),
3783
+ this.options.localDescriptor?.(meshName)
3784
+ );
3765
3785
  const entrySpotId =
3766
3786
  expected.allocation.objectKind === 'actor' && membership === undefined
3767
3787
  ? this.options.spotManager()?.entrySpotIdForMesh(meshName)
@@ -3788,18 +3808,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3788
3808
  objectGeneration: expected.objectGeneration,
3789
3809
  targetAttemptGeneration: prepare.targetAttemptGeneration,
3790
3810
  coordinatorExpectedStoreVersion: expected.storeVersion.value,
3791
- ...(membership === undefined
3792
- ? expected.allocation.objectKind === 'actor'
3811
+ ...(membership ??
3812
+ (expected.allocation.objectKind === 'actor'
3793
3813
  ? {
3794
3814
  actorSpotId: entrySpotId!,
3795
3815
  actorSpotGeneration: prepare.target.nodeGeneration,
3796
3816
  actorSpotKind: ZLinkSpotKind.Entry as const
3797
3817
  }
3798
- : {}
3799
- : {
3800
- ...membership,
3801
- actorSpotKind: ZLinkSpotKind.User as const
3802
- })
3818
+ : {}))
3803
3819
  }),
3804
3820
  membershipMutation: encodeMembershipMutation(envelope.memberships, participant.key)
3805
3821
  };
@@ -3934,6 +3950,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3934
3950
  failures.push(error);
3935
3951
  }
3936
3952
  stage.phase = 'failed';
3953
+ stage.releaseJoinAdmission();
3937
3954
  if (failures.length === 1) throw failures[0];
3938
3955
  if (failures.length > 1) {
3939
3956
  throw new AggregateError(failures, 'Relocation target rollback was incomplete.');
@@ -4098,6 +4115,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4098
4115
 
4099
4116
  /** Maps schema ordinals to the authoritative Location Store inventory. */
4100
4117
  private async resolveTargetParticipantInventory(
4118
+ meshName: string,
4101
4119
  request: ServiceMaintenanceRelocationPrepare,
4102
4120
  envelope: ServiceRelocationEnvelope,
4103
4121
  signal?: AbortSignal
@@ -4136,7 +4154,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4136
4154
  candidate.snapshot.payload,
4137
4155
  candidate.snapshot.objectGeneration
4138
4156
  );
4139
- if (actor?.spotId === request.object.spotId) {
4157
+ if (
4158
+ actor?.spotId === request.object.spotId &&
4159
+ actor.spotGeneration === request.object.objectGeneration
4160
+ ) {
4140
4161
  entries.push(candidate);
4141
4162
  }
4142
4163
  }
@@ -4184,21 +4205,23 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4184
4205
  authorityOwnerGeneration: snapshot.authorityOwnerGeneration
4185
4206
  };
4186
4207
  });
4187
- // Actor-root saved work is admitted against the destination membership.
4188
- // The canonical root intentionally omits a membership vector; meanwhile
4189
- // Location Store still names the source Spot until target cutover. Rebuild
4190
- // the Actor lane from the already accepted ActorJoin admission, preserving
4191
- // the target queue boundary for decoded saved-work.
4208
+ // Join recovery uses its accepted target membership. Maintenance Actor
4209
+ // roots restore into the target's startup Entry incarnation (§10.1).
4192
4210
  const memberships =
4193
4211
  request.object.kind === 'actor'
4194
- ? actorJoinTargetMembership(manager, envelope.aggregateId, participants)
4212
+ ? actorRootTargetMembership(
4213
+ manager,
4214
+ envelope.aggregateId,
4215
+ participants,
4216
+ this.options.localDescriptor?.(meshName)
4217
+ )
4195
4218
  : entries
4196
4219
  .filter((entry) => entry.snapshot.allocation.objectKind === 'actor')
4197
4220
  .map((entry) => ({
4198
4221
  actorKey: entry.key.value,
4199
4222
  spotKey: primaryKey.value,
4200
4223
  spotObjectGeneration: primary.objectGeneration,
4201
- membershipEpoch: 1n
4224
+ membershipEpoch: INITIAL_MEMBERSHIP_EPOCH
4202
4225
  }));
4203
4226
  return {
4204
4227
  ...envelope,
@@ -4482,14 +4505,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4482
4505
  meshName: string,
4483
4506
  targetNodeRid: RoutingId,
4484
4507
  frames: readonly Uint8Array[],
4485
- timeoutMs: number
4508
+ timeoutMs: number,
4509
+ signal?: AbortSignal,
4510
+ onSubmitted?: (signal: AbortSignal) => Promise<void>
4486
4511
  ): Promise<readonly Uint8Array[]> {
4487
4512
  const node = this.requireMeshNode(meshName);
4488
4513
  const request = node.requestInfrastructureControlFrames;
4489
4514
  if (request === undefined) {
4490
4515
  throw new Error('RouteMesh backend cannot submit bare infrastructure requests.');
4491
4516
  }
4492
- return await request.call(node, targetNodeRid, frames, { timeoutMs });
4517
+ return await request.call(node, targetNodeRid, frames, { timeoutMs, signal, onSubmitted });
4493
4518
  }
4494
4519
  }
4495
4520
 
@@ -4514,13 +4539,28 @@ function actorJoinAdmissionIdentity(envelope: ServiceRelocationEnvelope): string
4514
4539
  );
4515
4540
  }
4516
4541
 
4517
- function actorJoinTargetMembership(
4542
+ function actorRootTargetMembership(
4518
4543
  manager: DefaultZLinkSpotManager | undefined,
4519
4544
  relocationId: string,
4520
- participants: readonly ServiceRelocationParticipant[]
4545
+ participants: readonly ServiceRelocationParticipant[],
4546
+ localDescriptor: ZLinkMeshNodeDescriptor | undefined
4521
4547
  ): readonly ServiceRelocationMembership[] {
4522
4548
  const recovery = canonicalActorJoinRecovery(participants);
4523
- const admissionId = recovery?.request.handoffId ?? relocationId;
4549
+ const actor = participants.find((value) => value.objectKind === 'actor');
4550
+ if (recovery === undefined) {
4551
+ if (actor === undefined || localDescriptor?.entrySpotId === undefined) {
4552
+ throw new Error(`Actor relocation '${relocationId}' has no target Entry membership.`);
4553
+ }
4554
+ return [
4555
+ {
4556
+ actorKey: actor.key,
4557
+ spotKey: encodeAuthorityKey('user_spot', localDescriptor.entrySpotId).value,
4558
+ spotObjectGeneration: localDescriptor.lifecycleGeneration,
4559
+ membershipEpoch: INITIAL_MEMBERSHIP_EPOCH
4560
+ }
4561
+ ];
4562
+ }
4563
+ const admissionId = recovery.request.handoffId;
4524
4564
  const admission = (
4525
4565
  manager as unknown as
4526
4566
  | {
@@ -4538,7 +4578,6 @@ function actorJoinTargetMembership(
4538
4578
  }
4539
4579
  | undefined
4540
4580
  )?.formalRemoteActorAdmissions?.get(admissionId);
4541
- const actor = participants.find((value) => value.objectKind === 'actor');
4542
4581
  if (admission === undefined || actor === undefined) {
4543
4582
  throw new Error(`Actor relocation '${relocationId}' has no accepted target membership.`);
4544
4583
  }
@@ -4570,18 +4609,19 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
4570
4609
  );
4571
4610
  if (membership === undefined)
4572
4611
  throw new Error(`Actor '${identity}' relocation membership is missing.`);
4573
- const spotIdentity = decodeAuthorityKey({
4574
- value: membership.spotKey
4575
- } as ZLinkAuthorityKey).globalId;
4576
- const localDescriptor = this.options.localDescriptor?.(this.meshName);
4577
- const nativeEntrySpot = localDescriptor?.entrySpotId === spotIdentity;
4612
+ const targetMembership = actorMembershipTarget(
4613
+ membership,
4614
+ this.options.localDescriptor?.(this.meshName)
4615
+ )!;
4578
4616
  const actor = await this.requireActorManager().prepareRelocationActor(
4579
4617
  identity,
4580
4618
  participant.stableType,
4581
4619
  participant.objectGeneration,
4582
- participant.authorityOwnerGeneration + 1n,
4583
- nativeEntrySpot ? undefined : (spotIdentity as RoutingId),
4584
- membership.spotObjectGeneration,
4620
+ participant.authorityOwnerGeneration,
4621
+ targetMembership.actorSpotKind === ZLinkSpotKind.Entry
4622
+ ? undefined
4623
+ : (targetMembership.actorSpotId as RoutingId),
4624
+ targetMembership.actorSpotGeneration,
4585
4625
  membership.membershipEpoch,
4586
4626
  signal
4587
4627
  );
@@ -4603,7 +4643,7 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
4603
4643
  registration.implementation as Type<ZLinkSpot | ZLinkInstanceSpot>,
4604
4644
  identity as RoutingId,
4605
4645
  participant.objectGeneration,
4606
- participant.authorityOwnerGeneration + 1n,
4646
+ participant.authorityOwnerGeneration,
4607
4647
  signal
4608
4648
  );
4609
4649
  return {
@@ -4654,9 +4694,11 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
4654
4694
  ): Promise<void> {
4655
4695
  for (const membership of memberships) {
4656
4696
  const actor = hidden.get(membership.actorKey)?.actor;
4657
- const spotIdentity = decodeAuthorityKey({
4658
- value: membership.spotKey
4659
- } as ZLinkAuthorityKey).globalId;
4697
+ const targetMembership = actorMembershipTarget(
4698
+ membership,
4699
+ this.options.localDescriptor?.(this.meshName)
4700
+ )!;
4701
+ const spotIdentity = targetMembership.actorSpotId;
4660
4702
  const spot =
4661
4703
  hidden.get(membership.spotKey)?.activation ??
4662
4704
  this.requireSpotManager().resolveRelocationActivation(
@@ -4665,9 +4707,7 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
4665
4707
  );
4666
4708
  if (actor === undefined) throw new Error('Relocation membership Actor staging is missing.');
4667
4709
  const state = this.requireActorManager().getState(actor.context.actorId)!;
4668
- const localDescriptor = this.options.localDescriptor?.(this.meshName);
4669
- const isEntryMembership = localDescriptor?.entrySpotId === spotIdentity;
4670
- if (isEntryMembership) {
4710
+ if (targetMembership.actorSpotKind === ZLinkSpotKind.Entry) {
4671
4711
  // Entry Spot identity is published as a Location membership key, but
4672
4712
  // it is not a User Spot activation in the Actor runtime. The native
4673
4713
  // restore already received the target MeshNode RID as its Entry
@@ -4675,7 +4715,12 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
4675
4715
  // local Entry Spot path after the target commit.
4676
4716
  state.clearJoinedSpot();
4677
4717
  } else {
4678
- state.setJoinedSpot(spotIdentity as RoutingId, spot?.spot, membership.membershipEpoch);
4718
+ state.setJoinedSpot(
4719
+ spotIdentity as RoutingId,
4720
+ spot?.spot,
4721
+ membership.membershipEpoch,
4722
+ membership.spotObjectGeneration
4723
+ );
4679
4724
  }
4680
4725
  spot?.commitActorJoin(actor);
4681
4726
  }
@@ -4944,14 +4989,22 @@ function rewriteAuthorityApplicationRoute(
4944
4989
  }
4945
4990
 
4946
4991
  function actorMembershipTarget(
4947
- envelope: ServiceRelocationEnvelope,
4948
- actorKey: string
4949
- ): { readonly actorSpotId: string; readonly actorSpotGeneration: bigint } | undefined {
4950
- const membership = envelope.memberships.find((value) => value.actorKey === actorKey);
4992
+ membership: ServiceRelocationMembership | undefined,
4993
+ localDescriptor: ZLinkMeshNodeDescriptor | undefined
4994
+ ):
4995
+ | {
4996
+ readonly actorSpotId: string;
4997
+ readonly actorSpotGeneration: bigint;
4998
+ readonly actorSpotKind: ZLinkSpotKind.Entry | ZLinkSpotKind.User;
4999
+ }
5000
+ | undefined {
4951
5001
  if (membership === undefined) return undefined;
5002
+ const spotId = decodeAuthorityKey({ value: membership.spotKey } as ZLinkAuthorityKey).globalId;
4952
5003
  return {
4953
- actorSpotId: decodeAuthorityKey({ value: membership.spotKey } as ZLinkAuthorityKey).globalId,
4954
- actorSpotGeneration: membership.spotObjectGeneration
5004
+ actorSpotId: spotId,
5005
+ actorSpotGeneration: membership.spotObjectGeneration,
5006
+ actorSpotKind:
5007
+ spotId === localDescriptor?.entrySpotId ? ZLinkSpotKind.Entry : ZLinkSpotKind.User
4955
5008
  };
4956
5009
  }
4957
5010
 
@@ -5705,7 +5758,7 @@ function canonicalQueuedFrozenRecord(
5705
5758
  objectGeneration: participant.objectGeneration,
5706
5759
  nodeRid: target.nodeRid,
5707
5760
  nodeGeneration: target.nodeGeneration,
5708
- authorityOwnerGeneration: participant.authorityOwnerGeneration + 1n,
5761
+ authorityOwnerGeneration: participant.authorityOwnerGeneration,
5709
5762
  ownerLeaseGeneration: target.ownerLeaseGeneration
5710
5763
  },
5711
5764
  operationId,
@@ -5904,16 +5957,6 @@ function replyRelayPendingKey(
5904
5957
  return JSON.stringify([ackTargetNodeRid, replyRelayIdentityKey(value)]);
5905
5958
  }
5906
5959
 
5907
- function isServiceWireCommand(payload: Uint8Array, command: number): boolean {
5908
- return (
5909
- payload.byteLength >= 5 &&
5910
- payload[0] === 0x5a &&
5911
- payload[1] === 0x4d &&
5912
- payload[2] === 1 &&
5913
- payload[3] === command
5914
- );
5915
- }
5916
-
5917
5960
  export function encodeHandoffQueuedMessages(
5918
5961
  backlog: readonly ZLinkActorHandoffPacket[]
5919
5962
  ): readonly ServiceRelocationQueuedMessage[] {
@@ -6000,97 +6043,58 @@ function toServiceTimer(
6000
6043
  };
6001
6044
  }
6002
6045
 
6003
- const PREPARE_SIDEBAND_MAGIC = 'ZLNI';
6004
- const PREPARE_SIDEBAND_VERSION = 1;
6005
- const PREPARE_SIDEBAND_HEADER_BYTES = 11;
6006
- const PREPARE_SIDEBAND_VERSION_OFFSET = 4;
6007
- const PREPARE_SIDEBAND_KEY_LENGTH_OFFSET = 5;
6008
- const PREPARE_SIDEBAND_PAYLOAD_LENGTH_OFFSET = 7;
6009
6046
  const ACTOR_SESSION_JOURNAL_VERSION = 1;
6010
6047
  const SERVICE_RELOCATION_REPLY_PACKET_NAME = 'zlink.relocation.reply';
6011
6048
 
6012
- /**
6013
- * NODE-INTERNAL Prepare sideband. Frame zero remains the exact canonical
6014
- * command-40 schema record; each following ZLNI frame carries one sealed
6015
- * actor-session journal keyed by its canonical participant authority key.
6016
- * Sessionless relocation remains a single-frame, wire-identical Prepare.
6017
- */
6018
- function encodePrepareSideband(envelope: ServiceRelocationEnvelope): readonly Buffer[] {
6019
- return envelope.participants
6020
- .filter((participant) => participant.boundSessionState.byteLength !== 0)
6021
- .map((participant) => {
6022
- const key = Buffer.from(participant.key, 'utf8');
6023
- const payload = Buffer.from(participant.boundSessionState);
6024
- if (key.byteLength === 0 || key.byteLength > 0xffff || payload.byteLength > 0xffff_ffff) {
6025
- throw new TypeError('Bound-session relocation sideband is out of bounds.');
6026
- }
6027
- const header = Buffer.allocUnsafe(PREPARE_SIDEBAND_HEADER_BYTES);
6028
- header.write(PREPARE_SIDEBAND_MAGIC, 0, 'ascii');
6029
- header[PREPARE_SIDEBAND_VERSION_OFFSET] = PREPARE_SIDEBAND_VERSION;
6030
- header.writeUInt16BE(key.byteLength, PREPARE_SIDEBAND_KEY_LENGTH_OFFSET);
6031
- header.writeUInt32BE(payload.byteLength, PREPARE_SIDEBAND_PAYLOAD_LENGTH_OFFSET);
6032
- return Buffer.concat([header, key, payload]);
6033
- });
6034
- }
6035
-
6036
- function decodePrepareSideband(frames: readonly Uint8Array[]):
6037
- | readonly {
6038
- readonly participantKey: string;
6039
- readonly payload: Buffer;
6040
- }[]
6041
- | undefined {
6042
- const result: Array<{ readonly participantKey: string; readonly payload: Buffer }> = [];
6043
- const keys = new Set<string>();
6044
- for (const frame of frames) {
6045
- const bytes = Buffer.from(frame);
6046
- if (
6047
- bytes.byteLength < PREPARE_SIDEBAND_HEADER_BYTES ||
6048
- bytes.subarray(0, PREPARE_SIDEBAND_VERSION_OFFSET).toString('ascii') !==
6049
- PREPARE_SIDEBAND_MAGIC ||
6050
- bytes[PREPARE_SIDEBAND_VERSION_OFFSET] !== PREPARE_SIDEBAND_VERSION
6051
- ) {
6052
- return undefined;
6053
- }
6054
- const keyLength = bytes.readUInt16BE(PREPARE_SIDEBAND_KEY_LENGTH_OFFSET);
6055
- const payloadLength = bytes.readUInt32BE(PREPARE_SIDEBAND_PAYLOAD_LENGTH_OFFSET);
6056
- if (
6057
- PREPARE_SIDEBAND_HEADER_BYTES + keyLength + payloadLength !== bytes.byteLength ||
6058
- keyLength === 0
6059
- )
6060
- return undefined;
6061
- const participantKey = bytes
6062
- .subarray(PREPARE_SIDEBAND_HEADER_BYTES, PREPARE_SIDEBAND_HEADER_BYTES + keyLength)
6063
- .toString('utf8');
6064
- if (keys.has(participantKey)) return undefined;
6065
- keys.add(participantKey);
6066
- result.push({
6067
- participantKey,
6068
- payload: Buffer.from(bytes.subarray(PREPARE_SIDEBAND_HEADER_BYTES + keyLength))
6069
- });
6070
- }
6071
- return result;
6072
- }
6073
-
6074
- function attachPrepareBoundSessions(
6049
+ function attachCanonicalBoundSessions(
6075
6050
  envelope: ServiceRelocationEnvelope,
6076
- sideband: ServiceMaintenanceRelocationPrepare['nodeInternalBoundSessions']
6051
+ request: ServiceMaintenanceRelocationPrepare,
6052
+ meshName: string
6077
6053
  ): ServiceRelocationEnvelope {
6078
- if (sideband === undefined || sideband.length === 0) return envelope;
6079
- const byKey = new Map(
6080
- sideband.map((value) => [value.participantKey, Buffer.from(value.payload)])
6081
- );
6082
- const participants = envelope.participants.map((participant) => {
6083
- const payload = byKey.get(participant.key);
6084
- if (payload === undefined) return participant;
6085
- if (participant.objectKind !== 'actor') {
6086
- throw new TypeError('Bound-session relocation sideband names a non-Actor participant.');
6087
- }
6088
- byKey.delete(participant.key);
6089
- return { ...participant, boundSessionState: payload };
6090
- });
6091
- if (byKey.size !== 0)
6092
- throw new TypeError('Bound-session relocation sideband names an unknown participant.');
6093
- return { ...envelope, participants };
6054
+ return {
6055
+ ...envelope,
6056
+ participants: envelope.participants.map((participant) => {
6057
+ const binding = participant.sessionBinding;
6058
+ if (binding === undefined) return participant;
6059
+ if (participant.objectKind !== 'actor')
6060
+ throw new TypeError('Session binding names a non-Actor participant.');
6061
+ const sessionNodeRid = decodeRoutingId(
6062
+ '',
6063
+ Buffer.from(binding.sessionOwnerNodeRid).toString('hex')
6064
+ );
6065
+ const sessionRid = decodeRoutingId('', Buffer.from(binding.sessionRid).toString('hex'));
6066
+ const session = {
6067
+ sessionOwnerNodeRid: sessionNodeRid as string,
6068
+ sessionOwnerNodeGeneration: binding.sessionOwnerNodeGeneration,
6069
+ sessionOwnerId: binding.sessionOwnerId,
6070
+ sessionOwnerLeaseGeneration: binding.sessionOwnerLeaseGeneration,
6071
+ sessionRid: sessionRid as string,
6072
+ bindingGeneration: binding.bindingGeneration
6073
+ };
6074
+ const target: ZLinkRemoteBoundSessionTarget = {
6075
+ routerChannelId: meshName,
6076
+ targetNodeRid: sessionNodeRid,
6077
+ spotId:
6078
+ request.object.kind === 'actor'
6079
+ ? ((participant.rootSpotId ?? '') as RoutingId)
6080
+ : (request.object.spotId as RoutingId),
6081
+ sessionNodeRid,
6082
+ sessionRid,
6083
+ sessionOwnerNodeGeneration: binding.sessionOwnerNodeGeneration,
6084
+ sessionOwnerId: binding.sessionOwnerId,
6085
+ sessionOwnerLeaseGeneration: binding.sessionOwnerLeaseGeneration,
6086
+ bindingGeneration: binding.bindingGeneration,
6087
+ previousAuthorityOwnerGeneration: participant.authorityOwnerGeneration,
6088
+ relocationSealId: envelope.aggregateId,
6089
+ serviceWireRelocation: {
6090
+ relocation: request.relocation,
6091
+ coordinator: request.coordinator,
6092
+ session
6093
+ }
6094
+ };
6095
+ return { ...participant, boundSessionState: encodeActorSession(target) };
6096
+ })
6097
+ };
6094
6098
  }
6095
6099
 
6096
6100
  function encodeActorSession(target: ZLinkRemoteBoundSessionTarget | undefined): Buffer {
@@ -6103,7 +6107,13 @@ function encodeActorSession(target: ZLinkRemoteBoundSessionTarget | undefined):
6103
6107
  spotId: String(target.spotId),
6104
6108
  sessionNodeRid:
6105
6109
  target.sessionNodeRid === undefined ? undefined : String(target.sessionNodeRid),
6110
+ sessionNodeRidHex:
6111
+ target.sessionNodeRid === undefined
6112
+ ? undefined
6113
+ : encodeRoutingIdStorageHex(target.sessionNodeRid),
6106
6114
  sessionRid: target.sessionRid === undefined ? undefined : String(target.sessionRid),
6115
+ sessionRidHex:
6116
+ target.sessionRid === undefined ? undefined : encodeRoutingIdStorageHex(target.sessionRid),
6107
6117
  sessionOwnerNodeGeneration: target.sessionOwnerNodeGeneration?.toString(),
6108
6118
  sessionOwnerId: target.sessionOwnerId,
6109
6119
  sessionOwnerLeaseGeneration: target.sessionOwnerLeaseGeneration?.toString(),
@@ -6126,12 +6136,18 @@ function encodeActorSession(target: ZLinkRemoteBoundSessionTarget | undefined):
6126
6136
  expectedAuthorityStoreVersion:
6127
6137
  target.serviceWireRelocation.coordinator.expectedAuthorityStoreVersion,
6128
6138
  sessionOwnerNodeRid: target.serviceWireRelocation.session.sessionOwnerNodeRid,
6139
+ sessionOwnerNodeRidHex: encodeRoutingIdStorageHex(
6140
+ target.serviceWireRelocation.session.sessionOwnerNodeRid as RoutingId
6141
+ ),
6129
6142
  sessionOwnerNodeGeneration:
6130
6143
  target.serviceWireRelocation.session.sessionOwnerNodeGeneration.toString(),
6131
6144
  sessionOwnerId: target.serviceWireRelocation.session.sessionOwnerId,
6132
6145
  sessionOwnerLeaseGeneration:
6133
6146
  target.serviceWireRelocation.session.sessionOwnerLeaseGeneration.toString(),
6134
6147
  sessionRid: target.serviceWireRelocation.session.sessionRid,
6148
+ sessionRidHex: encodeRoutingIdStorageHex(
6149
+ target.serviceWireRelocation.session.sessionRid as RoutingId
6150
+ ),
6135
6151
  bindingGeneration: target.serviceWireRelocation.session.bindingGeneration.toString()
6136
6152
  }
6137
6153
  }),
@@ -6158,9 +6174,11 @@ function decodeActorSession(payload: Uint8Array): ZLinkRemoteBoundSessionTarget
6158
6174
  targetNodeRid: value.targetNodeRid as RoutingId,
6159
6175
  spotId: value.spotId as RoutingId,
6160
6176
  ...(typeof value.sessionNodeRid === 'string'
6161
- ? { sessionNodeRid: value.sessionNodeRid as RoutingId }
6177
+ ? { sessionNodeRid: decodeRoutingId(value.sessionNodeRid, value.sessionNodeRidHex) }
6178
+ : {}),
6179
+ ...(typeof value.sessionRid === 'string'
6180
+ ? { sessionRid: decodeRoutingId(value.sessionRid, value.sessionRidHex) }
6162
6181
  : {}),
6163
- ...(typeof value.sessionRid === 'string' ? { sessionRid: value.sessionRid as RoutingId } : {}),
6164
6182
  ...(optionalBigInt('sessionOwnerNodeGeneration') === undefined
6165
6183
  ? {}
6166
6184
  : { sessionOwnerNodeGeneration: optionalBigInt('sessionOwnerNodeGeneration') }),
@@ -6229,11 +6247,17 @@ function decodeActorSessionServiceWireFence(
6229
6247
  expectedAuthorityStoreVersion: text('expectedAuthorityStoreVersion')
6230
6248
  },
6231
6249
  session: {
6232
- sessionOwnerNodeRid: text('sessionOwnerNodeRid'),
6250
+ sessionOwnerNodeRid: decodeRoutingId(
6251
+ typeof value.sessionOwnerNodeRid === 'string' ? value.sessionOwnerNodeRid : '',
6252
+ value.sessionOwnerNodeRidHex
6253
+ ) as string,
6233
6254
  sessionOwnerNodeGeneration: positive('sessionOwnerNodeGeneration'),
6234
6255
  sessionOwnerId: text('sessionOwnerId'),
6235
6256
  sessionOwnerLeaseGeneration: positive('sessionOwnerLeaseGeneration'),
6236
- sessionRid: text('sessionRid'),
6257
+ sessionRid: decodeRoutingId(
6258
+ typeof value.sessionRid === 'string' ? value.sessionRid : '',
6259
+ value.sessionRidHex
6260
+ ) as string,
6237
6261
  bindingGeneration: positive('bindingGeneration')
6238
6262
  }
6239
6263
  };