@zlink-systems/framework 0.19.0 → 0.20.0
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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Configuration/Builders.d.ts +0 -2
- package/dist/contracts/Configuration/Configs.d.ts +1 -0
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -2
- package/dist/contracts/Locations/Authority.d.ts +1 -7
- package/dist/runtime/actors/actor-authority-publication.js +25 -22
- package/dist/runtime/actors/actor-creation.js +30 -9
- package/dist/runtime/actors/actor-runtime-state.d.ts +3 -0
- package/dist/runtime/actors/index.d.ts +3 -0
- package/dist/runtime/actors/index.js +14 -3
- package/dist/runtime/admission.d.ts +2 -0
- package/dist/runtime/admission.js +11 -5
- package/dist/runtime/backend/contracts/index.d.ts +4 -1
- package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -0
- package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
- package/dist/runtime/backend/node/node-raw-binding-port.d.ts +2 -1
- package/dist/runtime/backend/node/node-raw-binding-port.js +12 -5
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +1 -1
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +6 -0
- package/dist/runtime/channels/channel-socket-options.js +7 -0
- package/dist/runtime/channels/channel-socket-registry.js +3 -0
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -1
- package/dist/runtime/foundation/service-stateful-runtime.js +1 -5
- package/dist/runtime/host/actor-placement-coordinator.d.ts +14 -5
- package/dist/runtime/host/actor-placement-coordinator.js +159 -47
- package/dist/runtime/host/index.d.ts +2 -1
- package/dist/runtime/host/index.js +77 -120
- package/dist/runtime/host/location-runtime-owner.js +4 -2
- package/dist/runtime/host/remote-actor-join-receiver.js +12 -3
- package/dist/runtime/locations/creation-operation-id.d.ts +4 -0
- package/dist/runtime/locations/creation-operation-id.js +12 -0
- package/dist/runtime/locations/in-memory-authority-store.js +8 -22
- package/dist/runtime/locations/location-store-repository.js +36 -59
- package/dist/runtime/locations/opaque-record-key.d.ts +5 -0
- package/dist/runtime/locations/opaque-record-key.js +16 -0
- package/dist/runtime/locations/runtime.d.ts +7 -0
- package/dist/runtime/locations/runtime.js +130 -42
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +1672 -0
- package/dist/runtime/protocol/service_wire_codec.generated.js +9169 -0
- package/dist/runtime/spots/spot-node-runtime-manager.js +2 -2
- package/package.json +2 -2
- package/src/contracts/Configuration/Builders.ts +0 -2
- package/src/contracts/Configuration/Configs.ts +1 -0
- package/src/contracts/Configuration/RegistrationTypes.ts +1 -2
- package/src/contracts/Locations/Authority.ts +1 -8
- package/src/runtime/actors/actor-authority-publication.ts +25 -22
- package/src/runtime/actors/actor-creation.ts +45 -11
- package/src/runtime/actors/actor-runtime-state.ts +4 -0
- package/src/runtime/actors/index.ts +32 -4
- package/src/runtime/admission.ts +8 -5
- package/src/runtime/backend/contracts/index.ts +4 -1
- package/src/runtime/backend/node/node-mesh-backend-adapter.ts +2 -1
- package/src/runtime/backend/node/node-raw-binding-port.ts +16 -5
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -1
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +7 -0
- package/src/runtime/channels/channel-socket-options.ts +9 -0
- package/src/runtime/channels/channel-socket-registry.ts +4 -0
- package/src/runtime/foundation/service-stateful-runtime.ts +2 -6
- package/src/runtime/host/actor-placement-coordinator.ts +216 -77
- package/src/runtime/host/index.ts +95 -135
- package/src/runtime/host/location-runtime-owner.ts +4 -2
- package/src/runtime/host/remote-actor-join-receiver.ts +15 -3
- package/src/runtime/locations/creation-operation-id.ts +11 -0
- package/src/runtime/locations/in-memory-authority-store.ts +7 -29
- package/src/runtime/locations/location-store-repository.ts +71 -69
- package/src/runtime/locations/opaque-record-key.ts +26 -0
- package/src/runtime/locations/runtime.ts +154 -60
- package/src/runtime/protocol/service_wire_codec.generated.ts +8302 -0
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/spot-node-runtime-manager.ts +2 -2
|
@@ -279,7 +279,6 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
279
279
|
private spotManager?: DefaultZLinkSpotManager;
|
|
280
280
|
private ownerLeaseRecoveryRuntime?: ZLinkLocationRuntime;
|
|
281
281
|
private ownerLeaseRecoveryHandler?: () => void;
|
|
282
|
-
private ownerLeaseFailureHandler?: () => void;
|
|
283
282
|
private registerUserSpotHandlers?: (runtime: ZLinkSpotNodeRuntimeManager) => void;
|
|
284
283
|
private readonly destroyedActorRefs = new Map<string, ActorRef>();
|
|
285
284
|
private readonly runtimeEventPublisher: ZLinkRuntimeEventPublisher;
|
|
@@ -287,7 +286,7 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
287
286
|
private readonly applicationJobQueue: ApplicationJobQueue;
|
|
288
287
|
private readonly capacityStatus: HostCapacityStatusProjection;
|
|
289
288
|
private readonly metricRegistrations: import('../diagnostics').ZLinkRuntimeMetricRegistration[] = [];
|
|
290
|
-
private readonly admission = new ZLinkRuntimeAdmissionGate();
|
|
289
|
+
private readonly admission = new ZLinkRuntimeAdmissionGate(() => this.ownerAdmissionOpen());
|
|
291
290
|
private cachedLocationSpotRouteResolver?: ZLinkSpotRouteResolver;
|
|
292
291
|
private actorClientLocationResolver?: ZLinkStoreLocationResolvers;
|
|
293
292
|
// Shared, runtime-mutable message-flow mode cell — installed once so
|
|
@@ -939,6 +938,14 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
939
938
|
: ZLinkFrameworkRelocationReason.RuntimeNotReady
|
|
940
939
|
});
|
|
941
940
|
}
|
|
941
|
+
if (!this.admission.acceptsNewWork) {
|
|
942
|
+
return Promise.resolve({
|
|
943
|
+
mode: options.mode,
|
|
944
|
+
effectiveTargetApplicationVersion,
|
|
945
|
+
outcome: ZLinkFrameworkRelocationOutcome.Blocked,
|
|
946
|
+
reason: ZLinkFrameworkRelocationReason.StoreUnavailable
|
|
947
|
+
});
|
|
948
|
+
}
|
|
942
949
|
if (hasUnsupportedManualTopology(this.options.registration)) {
|
|
943
950
|
return Promise.resolve({
|
|
944
951
|
mode: options.mode,
|
|
@@ -1526,103 +1533,45 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
1526
1533
|
spotNodeRuntime: ZLinkSpotNodeRuntimeManager,
|
|
1527
1534
|
channelRuntime: ZLinkChannelRuntimeManager
|
|
1528
1535
|
): void {
|
|
1529
|
-
let publishing = false;
|
|
1530
|
-
let lastPublishedOwnerToken = runtime.currentOwnerToken;
|
|
1531
|
-
let recoveryRequired = false;
|
|
1532
|
-
let recoveringServices: Promise<void> | undefined;
|
|
1533
|
-
let stoppingServices: Promise<void> | undefined;
|
|
1534
|
-
let transportFence = Promise.resolve();
|
|
1535
1536
|
const handler = () => {
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|| (ownerToken === lastPublishedOwnerToken && !recoveryRequired)
|
|
1541
|
-
) return;
|
|
1542
|
-
if (runtime.ownerLeaseUsable === false) return;
|
|
1543
|
-
publishing = true;
|
|
1544
|
-
const signal = this.executionState?.abortController.signal;
|
|
1545
|
-
const recoverServices = async (): Promise<void> => {
|
|
1546
|
-
if (stoppingServices !== undefined) {
|
|
1547
|
-
await stoppingServices;
|
|
1548
|
-
stoppingServices = undefined;
|
|
1549
|
-
}
|
|
1550
|
-
if (this.statefulAuthorityRoutes !== undefined) return;
|
|
1551
|
-
const store = this.locationOwner.currentStores?.locationStore;
|
|
1552
|
-
if (store === undefined || this.executionState === undefined) return;
|
|
1553
|
-
if (recoveringServices !== undefined) return await recoveringServices;
|
|
1554
|
-
recoveringServices = (async () => {
|
|
1555
|
-
if (this.requiresStatefulAuthorityRuntime()) {
|
|
1556
|
-
const routes = this.createStatefulAuthorityRoutes(store, spotNodeRuntime);
|
|
1557
|
-
await routes.start(this.executionState?.abortController.signal);
|
|
1558
|
-
this.statefulAuthorityRoutes = routes;
|
|
1559
|
-
}
|
|
1560
|
-
})();
|
|
1561
|
-
try {
|
|
1562
|
-
await recoveringServices;
|
|
1563
|
-
} finally {
|
|
1564
|
-
recoveringServices = undefined;
|
|
1565
|
-
}
|
|
1566
|
-
};
|
|
1567
|
-
void transportFence
|
|
1568
|
-
.then(() => spotNodeRuntime.publishMeshNodeState(
|
|
1569
|
-
ZLinkFrameworkRuntimeState.Preparing,
|
|
1570
|
-
signal
|
|
1571
|
-
))
|
|
1572
|
-
.then(() => recoverServices())
|
|
1573
|
-
.then(() => {
|
|
1574
|
-
const currentOwnerToken = runtime.currentOwnerToken;
|
|
1575
|
-
if (runtime.ownerLeaseUsable === false
|
|
1576
|
-
|| currentOwnerToken === undefined
|
|
1577
|
-
|| currentOwnerToken.ownerId !== ownerToken.ownerId
|
|
1578
|
-
|| currentOwnerToken.leaseGeneration !== ownerToken.leaseGeneration) {
|
|
1579
|
-
throw new Error('Owner lease changed during recovery.');
|
|
1580
|
-
}
|
|
1581
|
-
return spotNodeRuntime.publishMeshNodeState(this.runtimeState, signal);
|
|
1582
|
-
})
|
|
1583
|
-
.then(() => spotNodeRuntime.startLocationAutoConnect(signal))
|
|
1584
|
-
.then(() => channelRuntime.reclaimLocationOwnerRows(signal))
|
|
1585
|
-
.then(() => this.locationOwner.currentLifecycle?.reclaimOwnerRows() ?? Promise.resolve())
|
|
1586
|
-
.then(
|
|
1587
|
-
() => {
|
|
1588
|
-
lastPublishedOwnerToken = ownerToken;
|
|
1589
|
-
recoveryRequired = false;
|
|
1590
|
-
publishing = false;
|
|
1591
|
-
},
|
|
1592
|
-
error => {
|
|
1537
|
+
if (!this.ownerAdmissionOpen()) return;
|
|
1538
|
+
void this.resumeOwnerLeaseOwnedWork(runtime, spotNodeRuntime, channelRuntime)
|
|
1539
|
+
.catch(error => {
|
|
1540
|
+
runtime.closeOwnerLeaseAdmission(error);
|
|
1593
1541
|
this.runtimeOrPreStartErrorSink.reportRuntimeTaskException(
|
|
1594
1542
|
'owner lease recovery',
|
|
1595
1543
|
error
|
|
1596
1544
|
);
|
|
1597
|
-
|
|
1598
|
-
}
|
|
1599
|
-
);
|
|
1545
|
+
});
|
|
1600
1546
|
};
|
|
1601
1547
|
this.ownerLeaseRecoveryRuntime = runtime;
|
|
1602
1548
|
this.ownerLeaseRecoveryHandler = handler;
|
|
1603
|
-
const failureHandler = () => {
|
|
1604
|
-
recoveryRequired = true;
|
|
1605
|
-
if (runtime.ownerLeaseUsable) return;
|
|
1606
|
-
// A Store failure invalidates new ownership and discovery work, but it
|
|
1607
|
-
// must not tear down transports that were already established. The
|
|
1608
|
-
// auto-connect reconciler applies storeFailureGraceMs to the last
|
|
1609
|
-
// complete descriptor set; fencing here would bypass that contract and
|
|
1610
|
-
// make existing requests fail during the grace window.
|
|
1611
|
-
const routes = this.statefulAuthorityRoutes;
|
|
1612
|
-
if (routes === undefined) return;
|
|
1613
|
-
this.statefulAuthorityRoutes = undefined;
|
|
1614
|
-
stoppingServices = routes.stop();
|
|
1615
|
-
void stoppingServices.catch(error =>
|
|
1616
|
-
this.runtimeOrPreStartErrorSink.reportRuntimeTaskException(
|
|
1617
|
-
'stateful authority route fencing',
|
|
1618
|
-
error
|
|
1619
|
-
));
|
|
1620
|
-
};
|
|
1621
|
-
this.ownerLeaseFailureHandler = failureHandler;
|
|
1622
1549
|
runtime.addOwnerLeaseRenewedHandler(handler);
|
|
1623
|
-
|
|
1624
|
-
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
private async resumeOwnerLeaseOwnedWork(
|
|
1553
|
+
runtime: ZLinkLocationRuntime,
|
|
1554
|
+
spotNodeRuntime: ZLinkSpotNodeRuntimeManager,
|
|
1555
|
+
channelRuntime: ZLinkChannelRuntimeManager
|
|
1556
|
+
): Promise<void> {
|
|
1557
|
+
if (!this.ownerAdmissionOpen()) return;
|
|
1558
|
+
const signal = this.executionState?.abortController.signal;
|
|
1559
|
+
await spotNodeRuntime.publishMeshNodeState(ZLinkFrameworkRuntimeState.Preparing, signal);
|
|
1560
|
+
const store = this.locationOwner.currentStores?.locationStore;
|
|
1561
|
+
if (this.statefulAuthorityRoutes === undefined
|
|
1562
|
+
&& store !== undefined
|
|
1563
|
+
&& this.executionState !== undefined
|
|
1564
|
+
&& this.requiresStatefulAuthorityRuntime()) {
|
|
1565
|
+
const routes = this.createStatefulAuthorityRoutes(store, spotNodeRuntime);
|
|
1566
|
+
await routes.start(signal);
|
|
1567
|
+
this.statefulAuthorityRoutes = routes;
|
|
1625
1568
|
}
|
|
1569
|
+
if (runtime.ownerLeaseUsable === false) return;
|
|
1570
|
+
await spotNodeRuntime.publishMeshNodeState(this.runtimeState, signal);
|
|
1571
|
+
await spotNodeRuntime.startLocationAutoConnect(signal);
|
|
1572
|
+
await channelRuntime.startLocationAutoConnect(signal);
|
|
1573
|
+
await channelRuntime.reclaimLocationOwnerRows(signal);
|
|
1574
|
+
await this.locationOwner.currentLifecycle?.reclaimOwnerRows();
|
|
1626
1575
|
}
|
|
1627
1576
|
|
|
1628
1577
|
private createStatefulAuthorityRoutes(
|
|
@@ -1715,14 +1664,14 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
1715
1664
|
if (this.ownerLeaseRecoveryRuntime !== undefined && this.ownerLeaseRecoveryHandler !== undefined) {
|
|
1716
1665
|
this.ownerLeaseRecoveryRuntime.removeOwnerLeaseRenewedHandler(this.ownerLeaseRecoveryHandler);
|
|
1717
1666
|
}
|
|
1718
|
-
if (this.ownerLeaseRecoveryRuntime !== undefined && this.ownerLeaseFailureHandler !== undefined) {
|
|
1719
|
-
if (typeof this.ownerLeaseRecoveryRuntime.removeOwnerLeaseRenewalFailedHandler === 'function') {
|
|
1720
|
-
this.ownerLeaseRecoveryRuntime.removeOwnerLeaseRenewalFailedHandler(this.ownerLeaseFailureHandler);
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
1667
|
this.ownerLeaseRecoveryRuntime = undefined;
|
|
1724
1668
|
this.ownerLeaseRecoveryHandler = undefined;
|
|
1725
|
-
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
private ownerAdmissionOpen(): boolean {
|
|
1672
|
+
const runtime = this.locationOwner.currentRuntime;
|
|
1673
|
+
if (runtime !== undefined) return runtime.ownerLeaseUsable;
|
|
1674
|
+
return this.locationOwner.currentStores?.locationStore === undefined;
|
|
1726
1675
|
}
|
|
1727
1676
|
|
|
1728
1677
|
async onApplicationBootstrap(): Promise<void> {
|
|
@@ -2068,14 +2017,7 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
2068
2017
|
detachedTaskRunner: this.detachedTaskRunner(),
|
|
2069
2018
|
metrics: this.metrics,
|
|
2070
2019
|
admission: this.admission,
|
|
2071
|
-
statefulExecutionAllowed: () =>
|
|
2072
|
-
const locationRuntime = this.locationOwner.currentRuntime;
|
|
2073
|
-
if (locationRuntime !== undefined) return locationRuntime.ownerLeaseUsable;
|
|
2074
|
-
// A host configured with a Location Store must not execute stateful
|
|
2075
|
-
// timers while its lease runtime is unavailable. Hosts without a
|
|
2076
|
-
// Location Store have no owner lease to fence.
|
|
2077
|
-
return this.locationOwner.currentStores?.locationStore === undefined;
|
|
2078
|
-
}
|
|
2020
|
+
statefulExecutionAllowed: () => this.ownerAdmissionOpen()
|
|
2079
2021
|
}).create(this.actorTransferRuntime),
|
|
2080
2022
|
activationConcurrencyLimitProvider: (meshName: string) =>
|
|
2081
2023
|
this.options.registration.spotNodes.get(meshName)?.activationConcurrencyLimit ?? 128,
|
|
@@ -2165,17 +2107,24 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
2165
2107
|
);
|
|
2166
2108
|
if (selected === undefined) return undefined;
|
|
2167
2109
|
const localStatus = meshNode?.status();
|
|
2110
|
+
if (localStatus === undefined) {
|
|
2111
|
+
throw createInternalFrameworkException(
|
|
2112
|
+
ZLinkFrameworkInternalErrorKind.ObjectClientNotConfigured,
|
|
2113
|
+
`RouteMesh '${meshName}' has no local client node.`
|
|
2114
|
+
);
|
|
2115
|
+
}
|
|
2168
2116
|
return {
|
|
2169
2117
|
meshName,
|
|
2170
2118
|
nodeRid: selected.rid,
|
|
2171
2119
|
nodeGeneration: selected.lifecycleGeneration,
|
|
2120
|
+
sourceNodeRid: String(localStatus.routingId),
|
|
2121
|
+
sourceNodeGeneration: localStatus.lifecycleGeneration,
|
|
2172
2122
|
entrySpotId: selected.entrySpotId!,
|
|
2173
2123
|
owner: {
|
|
2174
2124
|
ownerId: selected.ownerId,
|
|
2175
2125
|
leaseGeneration: selected.leaseGeneration
|
|
2176
2126
|
},
|
|
2177
|
-
isLocal: localStatus
|
|
2178
|
-
&& String(localStatus.routingId) === String(selected.rid)
|
|
2127
|
+
isLocal: String(localStatus.routingId) === String(selected.rid)
|
|
2179
2128
|
&& localStatus.lifecycleGeneration === selected.lifecycleGeneration
|
|
2180
2129
|
};
|
|
2181
2130
|
}
|
|
@@ -2394,6 +2343,13 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
2394
2343
|
requestPayload,
|
|
2395
2344
|
this.options.registration.messageSerializers
|
|
2396
2345
|
);
|
|
2346
|
+
const entrySpotId = this.spotNodeRuntime
|
|
2347
|
+
?.meshNodeDescriptor(meshName)?.entrySpotId;
|
|
2348
|
+
if (entrySpotId === undefined) {
|
|
2349
|
+
throw new ZLinkConfigurationException(
|
|
2350
|
+
`Remote Actor creation requires the '${meshName}' MeshNode Entry Spot descriptor.`
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2397
2353
|
const entrySpot = node.entrySpot().status();
|
|
2398
2354
|
const nativeActorRef = node.restoreActorAuthority?.(
|
|
2399
2355
|
record.actorId,
|
|
@@ -2416,6 +2372,9 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
2416
2372
|
createSignal,
|
|
2417
2373
|
nativeActorRef
|
|
2418
2374
|
);
|
|
2375
|
+
if (local.status === 'failed') {
|
|
2376
|
+
return { result: 'failed' as const, error: local.error };
|
|
2377
|
+
}
|
|
2419
2378
|
if (local.status === 'rejected') {
|
|
2420
2379
|
let reply: Buffer | undefined;
|
|
2421
2380
|
if (local.reply !== undefined) {
|
|
@@ -2461,7 +2420,7 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
2461
2420
|
...local.actorRef,
|
|
2462
2421
|
meshName
|
|
2463
2422
|
},
|
|
2464
|
-
entrySpotId
|
|
2423
|
+
entrySpotId,
|
|
2465
2424
|
entrySpotGeneration: entrySpot.lifecycleGeneration,
|
|
2466
2425
|
...(reply === undefined ? {} : { reply }),
|
|
2467
2426
|
onPublished: () => actors.adoptCreatedAuthority(
|
|
@@ -2849,37 +2808,38 @@ export class ZLinkFrameworkRuntimeHost implements
|
|
|
2849
2808
|
}
|
|
2850
2809
|
case ReceiveKind.NodeSend:
|
|
2851
2810
|
case ReceiveKind.NodeRequest: {
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
if (
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2811
|
+
return this.admission.run(meshName, 'RouteMesh node dispatch', async () => {
|
|
2812
|
+
if (await this.serviceRelocation.tryHandleControl(meshName, record, signal)) {
|
|
2813
|
+
return;
|
|
2814
|
+
}
|
|
2815
|
+
if (record.kind === ReceiveKind.NodeRequest && record.parts.length === 1) {
|
|
2816
|
+
const terminal = decodeRemoteActorSourceLeaveTerminal(record.parts[0]!.data());
|
|
2817
|
+
if (terminal !== undefined) {
|
|
2818
|
+
if (
|
|
2819
|
+
await this.spotManager?.completeFormalSourceLeaveTerminal(
|
|
2820
|
+
terminal.actorId,
|
|
2821
|
+
terminal.transferId,
|
|
2822
|
+
terminal.succeeded
|
|
2823
|
+
) !== true
|
|
2824
|
+
) {
|
|
2825
|
+
throw new ZLinkConfigurationException(
|
|
2826
|
+
`Actor '${terminal.actorId}' has no matching formal transfer terminal gate.`
|
|
2827
|
+
);
|
|
2828
|
+
}
|
|
2829
|
+
if (record.reply(Buffer.alloc(0)) !== SubmitResult.Ok) {
|
|
2830
|
+
throw new ZLinkConfigurationException(
|
|
2831
|
+
`Actor '${terminal.actorId}' formal transfer terminal acknowledgement failed.`
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2834
|
+
return;
|
|
2873
2835
|
}
|
|
2874
|
-
return Promise.resolve();
|
|
2875
2836
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
channelRuntime.dispatchMeshRoute(meshName, record, signal));
|
|
2837
|
+
const channelRuntime = this.channelRuntime;
|
|
2838
|
+
if (channelRuntime === undefined) {
|
|
2839
|
+
throw new ZLinkConfigurationException('MeshNode node-direct dispatch requires the channel runtime.');
|
|
2840
|
+
}
|
|
2841
|
+
return await channelRuntime.dispatchMeshRoute(meshName, record, signal);
|
|
2842
|
+
});
|
|
2883
2843
|
}
|
|
2884
2844
|
case ReceiveKind.SpotSend:
|
|
2885
2845
|
case ReceiveKind.SpotRequest:
|
|
@@ -212,14 +212,16 @@ export class ZLinkLocationRuntimeOwner {
|
|
|
212
212
|
this.options.registration.locations.options,
|
|
213
213
|
this.currentEvents
|
|
214
214
|
);
|
|
215
|
-
await spotNodeRuntime.startLocationAutoConnect();
|
|
216
215
|
channelRuntime.configureLocationAutoConnect(
|
|
217
216
|
runtime,
|
|
218
217
|
stores,
|
|
219
218
|
this.options.registration.locations.options,
|
|
220
219
|
this.currentEvents
|
|
221
220
|
);
|
|
222
|
-
|
|
221
|
+
if (runtime.ownerLeaseUsable) {
|
|
222
|
+
await spotNodeRuntime.startLocationAutoConnect();
|
|
223
|
+
await channelRuntime.startLocationAutoConnect();
|
|
224
|
+
}
|
|
223
225
|
return runtime;
|
|
224
226
|
}
|
|
225
227
|
|
|
@@ -215,9 +215,21 @@ function hasAuthorityFence(
|
|
|
215
215
|
readonly expectedAuthorityOwnerGeneration: string;
|
|
216
216
|
readonly expectedOwnerLeaseGeneration: string;
|
|
217
217
|
} {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
const fields = [
|
|
219
|
+
join.actorNodeGeneration,
|
|
220
|
+
join.expectedAuthorityOwnerGeneration,
|
|
221
|
+
join.expectedOwnerLeaseGeneration
|
|
222
|
+
];
|
|
223
|
+
if (fields.every(value => value === undefined)) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
if (fields.some(value => value === undefined)) {
|
|
227
|
+
throw createInternalFrameworkException(
|
|
228
|
+
ZLinkFrameworkInternalErrorKind.RequestProtocolError,
|
|
229
|
+
`Actor '${join.actorId}' Join carries an incomplete Authority fence.`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return true;
|
|
221
233
|
}
|
|
222
234
|
|
|
223
235
|
function decodeJoinAuthorityFence(join: JoinAuthorityFence): {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export function randomOperationId(
|
|
4
|
+
entropy: Uint8Array = randomBytes(16)
|
|
5
|
+
): { readonly high: bigint; readonly low: bigint } {
|
|
6
|
+
if (entropy.byteLength !== 16) throw new RangeError('Operation ID entropy must be 16 bytes.');
|
|
7
|
+
const bytes = Buffer.from(entropy);
|
|
8
|
+
const high = bytes.readBigUInt64BE(0);
|
|
9
|
+
const low = bytes.readBigUInt64BE(8);
|
|
10
|
+
return high === 0n && low === 0n ? { high, low: 1n } : { high, low };
|
|
11
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import type {
|
|
3
3
|
ZLinkAggregateAbortResult,
|
|
4
4
|
ZLinkAggregateCommitResult,
|
|
@@ -31,6 +31,7 @@ import type {
|
|
|
31
31
|
ZLinkPlacementAllocation
|
|
32
32
|
} from './internal-location-contracts';
|
|
33
33
|
import { encodeAuthorityKey } from './authority-key-codec';
|
|
34
|
+
import { creationTerminalPreimage } from './opaque-record-key';
|
|
34
35
|
|
|
35
36
|
const MAX_GENERATION = 0x7fff_ffff_ffff_ffffn;
|
|
36
37
|
const MAX_PAYLOAD_BYTES = 1024 * 1024;
|
|
@@ -394,9 +395,6 @@ export class ZLinkInMemoryAuthorityStore {
|
|
|
394
395
|
}
|
|
395
396
|
const terminalRecord = validateTerminalForMutation(
|
|
396
397
|
request.completion.terminal,
|
|
397
|
-
request.completion.kind,
|
|
398
|
-
request.reservationId,
|
|
399
|
-
request.key.kind,
|
|
400
398
|
this.now()
|
|
401
399
|
)!;
|
|
402
400
|
const existingTerminal = this.operationTerminals.get(
|
|
@@ -486,7 +484,7 @@ export class ZLinkInMemoryAuthorityStore {
|
|
|
486
484
|
this.operationTerminals.delete(key);
|
|
487
485
|
return { kind: 'missing', storeNow: this.now() };
|
|
488
486
|
}
|
|
489
|
-
return { kind: 'found',
|
|
487
|
+
return { kind: 'found', terminalEnvelope: Buffer.from(record.terminalEnvelope) };
|
|
490
488
|
}
|
|
491
489
|
|
|
492
490
|
private creationTerminalAvailable(record: ZLinkCreationTerminalRecord): boolean {
|
|
@@ -843,7 +841,7 @@ function validateCreationOperation(operation: ZLinkCreationOperationIdentity): v
|
|
|
843
841
|
}
|
|
844
842
|
if (
|
|
845
843
|
operation.sourceNodeGeneration < 1n
|
|
846
|
-
|| operation.sourceNodeGeneration >
|
|
844
|
+
|| operation.sourceNodeGeneration > MAX_U64
|
|
847
845
|
|| operation.operationId.high < 0n
|
|
848
846
|
|| operation.operationId.high > MAX_U64
|
|
849
847
|
|| operation.operationId.low < 0n
|
|
@@ -856,22 +854,12 @@ function validateCreationOperation(operation: ZLinkCreationOperationIdentity): v
|
|
|
856
854
|
|
|
857
855
|
function validateTerminalForMutation(
|
|
858
856
|
publication: ZLinkCreationTerminalPublication | undefined,
|
|
859
|
-
state: ZLinkCreationTerminalRecord['state'],
|
|
860
|
-
reservationId: string,
|
|
861
|
-
objectKind: ZLinkPlacementAllocation['objectKind'],
|
|
862
857
|
storeNow: Date
|
|
863
858
|
): ZLinkCreationTerminalRecord | undefined {
|
|
864
859
|
if (publication === undefined) return undefined;
|
|
865
860
|
validateCreationOperation(publication.operation);
|
|
866
|
-
if (
|
|
867
|
-
|
|
868
|
-
|| publication.terminalEnvelopeSha256.byteLength !== 32
|
|
869
|
-
) {
|
|
870
|
-
throw new RangeError('Creation terminal envelope must not exceed 1 MiB and requires a SHA-256 digest.');
|
|
871
|
-
}
|
|
872
|
-
const actualSha = createHash('sha256').update(publication.terminalEnvelope).digest();
|
|
873
|
-
if (!timingSafeEqual(actualSha, Buffer.from(publication.terminalEnvelopeSha256))) {
|
|
874
|
-
throw new TypeError('Creation terminal envelope SHA-256 does not match its bytes.');
|
|
861
|
+
if (publication.terminalEnvelope.byteLength > MAX_PAYLOAD_BYTES) {
|
|
862
|
+
throw new RangeError('Creation terminal envelope must not exceed 1 MiB.');
|
|
875
863
|
}
|
|
876
864
|
const deadlineMs = publication.operationDeadline.getTime();
|
|
877
865
|
const expiresAtMs = deadlineMs + CREATION_TERMINAL_RETENTION_MS;
|
|
@@ -883,24 +871,15 @@ function validateTerminalForMutation(
|
|
|
883
871
|
throw new RangeError('Creation terminal expiry must be the live operation deadline plus five minutes.');
|
|
884
872
|
}
|
|
885
873
|
return {
|
|
886
|
-
state,
|
|
887
874
|
operation: copyCreationOperation(publication.operation),
|
|
888
|
-
reservationId: requireText(reservationId, 'creation reservation ID'),
|
|
889
|
-
objectKind,
|
|
890
875
|
terminalEnvelope: Buffer.from(publication.terminalEnvelope),
|
|
891
|
-
terminalEnvelopeSha256: Buffer.from(publication.terminalEnvelopeSha256),
|
|
892
876
|
expiresAt: new Date(expiresAtMs),
|
|
893
877
|
storeNow: new Date(storeNow.getTime())
|
|
894
878
|
};
|
|
895
879
|
}
|
|
896
880
|
|
|
897
881
|
function creationOperationKey(operation: ZLinkCreationOperationIdentity): string {
|
|
898
|
-
return
|
|
899
|
-
String(operation.sourceNodeRid),
|
|
900
|
-
operation.sourceNodeGeneration.toString(),
|
|
901
|
-
operation.operationId.high.toString(16).padStart(16, '0'),
|
|
902
|
-
operation.operationId.low.toString(16).padStart(16, '0')
|
|
903
|
-
].join('\0');
|
|
882
|
+
return creationTerminalPreimage(operation);
|
|
904
883
|
}
|
|
905
884
|
|
|
906
885
|
function copyCreationOperation(
|
|
@@ -921,7 +900,6 @@ function copyTerminalRecord(record: ZLinkCreationTerminalRecord): ZLinkCreationT
|
|
|
921
900
|
...record,
|
|
922
901
|
operation: copyCreationOperation(record.operation),
|
|
923
902
|
terminalEnvelope: Buffer.from(record.terminalEnvelope),
|
|
924
|
-
terminalEnvelopeSha256: Buffer.from(record.terminalEnvelopeSha256),
|
|
925
903
|
expiresAt: new Date(record.expiresAt),
|
|
926
904
|
storeNow: new Date(record.storeNow)
|
|
927
905
|
};
|