@serve.zone/interfaces 31.7.0 → 31.8.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/changelog.md +32 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/index.d.ts +1 -0
- package/dist_ts/data/index.js +2 -1
- package/dist_ts/data/networkcredential.d.ts +10 -4
- package/dist_ts/data/networkcredential.js +14 -8
- package/dist_ts/data/networkdelivery.d.ts +21 -0
- package/dist_ts/data/networkdelivery.js +35 -7
- package/dist_ts/data/networklease.d.ts +110 -0
- package/dist_ts/data/networklease.js +159 -0
- package/dist_ts/data/networkprojection.d.ts +10 -5
- package/dist_ts/data/networkprojection.js +15 -22
- package/dist_ts/data/networksigning.js +5 -4
- package/dist_ts/private/networkcontract.d.ts +16 -0
- package/dist_ts/private/networkcontract.js +31 -1
- package/dist_ts/requests/cluster.d.ts +16 -0
- package/dist_ts/requests/cluster.js +1 -1
- package/dist_ts/requests/runtimesession.d.ts +14 -0
- package/dist_ts/runtime.networkprojection.d.ts +14 -0
- package/dist_ts/runtime.networkprojection.js +68 -19
- package/package.json +2 -2
- package/readme.md +131 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/index.ts +1 -0
- package/ts/data/networkcredential.ts +19 -9
- package/ts/data/networkdelivery.ts +49 -6
- package/ts/data/networklease.ts +229 -0
- package/ts/data/networkprojection.ts +28 -26
- package/ts/data/networksigning.ts +5 -4
- package/ts/private/networkcontract.ts +34 -0
- package/ts/requests/cluster.ts +20 -0
- package/ts/requests/runtimesession.ts +17 -0
- package/ts/runtime.networkprojection.ts +87 -20
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import type { TSha256Digest } from './immutableimage.js';
|
|
2
|
+
import type { IRuntimeAuthorityReferenceV1 } from './runtimeassignment.js';
|
|
3
|
+
import type { IRuntimeNetworkDnsWindow, IRuntimeNetworkNodeScopeV1 } from './networkprojection.js';
|
|
4
|
+
import { snapshotRuntimeNetworkNodeScopeV1, snapshotRuntimeNetworkProjectionV1, validateRuntimeNetworkProjectionV1 } from './networkprojection.js';
|
|
5
|
+
import { captureNetwork, failNetwork, networkCounter, networkDigest, networkDnsWindow, networkEffectiveDnsWindow, networkEqual,
|
|
6
|
+
networkHash, networkId, networkList, networkMaximumDnsLeaseMs, networkRecord, networkReference, networkReferenceOf,
|
|
7
|
+
networkRenewsProjection, networkSafe, networkSameScope, networkScope, networkSignature, requireNetwork } from '../private/networkcontract.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A fresh DNS window for one exact admitted projection, and nothing else.
|
|
11
|
+
*
|
|
12
|
+
* A projection's window is signed content, so only a newly signed statement can extend it. A
|
|
13
|
+
* re-issued projection would do that too, but a receiver treats every new projection as new
|
|
14
|
+
* network state and applies it again; a renewal leaves the applied state alone. It names the
|
|
15
|
+
* projection by exact reference, so it never joins the projection chain and stops being
|
|
16
|
+
* admissible the moment a successor projection is admitted.
|
|
17
|
+
*/
|
|
18
|
+
export interface IRuntimeNetworkDnsLeaseRenewal extends IRuntimeNetworkNodeScopeV1 {
|
|
19
|
+
projection: IRuntimeAuthorityReferenceV1;
|
|
20
|
+
/** Orders the renewals of one projection. A receiver may skip sequences, never go back. */
|
|
21
|
+
sequence: number;
|
|
22
|
+
/**
|
|
23
|
+
* `issuedAt` is the start of the window's slot, not the signing instant: a controller signs a
|
|
24
|
+
* run of future windows ahead of time, and the fifteen-minute rule is measured from here.
|
|
25
|
+
*/
|
|
26
|
+
dns: IRuntimeNetworkDnsWindow;
|
|
27
|
+
digest: TSha256Digest;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Names one renewal without its window: what a receiver records and what an ACK repeats. */
|
|
31
|
+
export interface IRuntimeNetworkDnsLeaseRenewalReference {
|
|
32
|
+
projection: IRuntimeAuthorityReferenceV1;
|
|
33
|
+
sequence: number;
|
|
34
|
+
digest: TSha256Digest;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ISignedRuntimeNetworkDnsLeaseRenewal {
|
|
38
|
+
/** The signing authority revision that also signs this controller's projections. */
|
|
39
|
+
authority: IRuntimeAuthorityReferenceV1;
|
|
40
|
+
renewal: IRuntimeNetworkDnsLeaseRenewal;
|
|
41
|
+
/** Ed25519 signature of the complete domain-separated envelope, lowercase hex. */
|
|
42
|
+
signature: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The renewals a cluster relay holds for one node it carries, replacing what it held before.
|
|
47
|
+
*
|
|
48
|
+
* The relay has no signing trust and verifies no signature: it holds signed statements and hands
|
|
49
|
+
* them to the node, which verifies each one against its own pinned authority.
|
|
50
|
+
*/
|
|
51
|
+
export interface IHoldRuntimeNetworkDnsLeaseRenewalsRequest {
|
|
52
|
+
/** The relay's own socket names the cluster; one relay carries every node of it, so the node is named here. */
|
|
53
|
+
nodeId: string;
|
|
54
|
+
projection: IRuntimeAuthorityReferenceV1;
|
|
55
|
+
/** Ascending by sequence. Empty holds nothing for this node. */
|
|
56
|
+
renewals: ISignedRuntimeNetworkDnsLeaseRenewal[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface IHoldRuntimeNetworkDnsLeaseRenewalsResponse {
|
|
60
|
+
held: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const runtimeNetworkDnsLeaseRenewalContract = Object.freeze({
|
|
64
|
+
renewalDigestDomain: 'serve.zone/runtime-network-dns-lease-renewal',
|
|
65
|
+
signatureDomain: 'serve.zone/runtime-network-dns-lease-renewal-signature',
|
|
66
|
+
maximumDnsLeaseMs: networkMaximumDnsLeaseMs,
|
|
67
|
+
maximumEscrowedRenewals: 32,
|
|
68
|
+
/** From the first held window's `issuedAt` to the last one's `expiresAt`. */
|
|
69
|
+
maximumEscrowHorizonMs: 14_400_000,
|
|
70
|
+
binding: 'exact-projection-reference',
|
|
71
|
+
supersession: 'a-successor-projection-refuses-every-renewal-of-its-predecessor',
|
|
72
|
+
sequence: 'strictly-increasing-per-projection-skipping-allowed',
|
|
73
|
+
effect: 'dns-window-only-never-applied-network-state',
|
|
74
|
+
escrowRelease: 'only-the-current-window-and-only-while-the-holder-has-no-controller-session',
|
|
75
|
+
revocationLatency: 'a-detached-cluster-keeps-dns-authority-until-its-last-held-window-ends',
|
|
76
|
+
} as const);
|
|
77
|
+
|
|
78
|
+
export const snapshotRuntimeNetworkDnsLeaseRenewal = (value: unknown): IRuntimeNetworkDnsLeaseRenewal => networkSafe(() => {
|
|
79
|
+
const item = networkRecord(captureNetwork(value), ['controller', 'nodeId', 'runtimeNamespace', 'projection', 'sequence', 'dns', 'digest']);
|
|
80
|
+
return { ...networkScope(item), projection: networkReference(item.projection), sequence: networkCounter(item.sequence),
|
|
81
|
+
dns: networkDnsWindow(item.dns), digest: networkDigest(item.digest) };
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export const snapshotRuntimeNetworkDnsLeaseRenewalReference = (value: unknown): IRuntimeNetworkDnsLeaseRenewalReference => networkSafe(() => {
|
|
85
|
+
const item = networkRecord(captureNetwork(value), ['projection', 'sequence', 'digest']);
|
|
86
|
+
return { projection: networkReference(item.projection), sequence: networkCounter(item.sequence), digest: networkDigest(item.digest) };
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const getRuntimeNetworkDnsLeaseRenewalReference = (value: IRuntimeNetworkDnsLeaseRenewal): IRuntimeNetworkDnsLeaseRenewalReference =>
|
|
90
|
+
({ projection: networkReferenceOf(value.projection), sequence: value.sequence, digest: value.digest });
|
|
91
|
+
|
|
92
|
+
export const snapshotSignedRuntimeNetworkDnsLeaseRenewal = (value: unknown): ISignedRuntimeNetworkDnsLeaseRenewal => networkSafe(() => {
|
|
93
|
+
const item = networkRecord(captureNetwork(value), ['authority', 'renewal', 'signature']);
|
|
94
|
+
return { authority: networkReference(item.authority), renewal: snapshotRuntimeNetworkDnsLeaseRenewal(item.renewal),
|
|
95
|
+
signature: networkSignature(item.signature) };
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export const computeRuntimeNetworkDnsLeaseRenewalDigest = (value: IRuntimeNetworkDnsLeaseRenewal): Promise<TSha256Digest> =>
|
|
99
|
+
networkHash(runtimeNetworkDnsLeaseRenewalContract.renewalDigestDomain, snapshotRuntimeNetworkDnsLeaseRenewal(value));
|
|
100
|
+
|
|
101
|
+
/** Structure and digest only. A digest authenticates nothing: verify the signature with stored trust. */
|
|
102
|
+
export const validateRuntimeNetworkDnsLeaseRenewal = async (value: unknown): Promise<IRuntimeNetworkDnsLeaseRenewal> => {
|
|
103
|
+
try {
|
|
104
|
+
const renewal = snapshotRuntimeNetworkDnsLeaseRenewal(value);
|
|
105
|
+
requireNetwork(renewal.digest === await computeRuntimeNetworkDnsLeaseRenewalDigest(renewal));
|
|
106
|
+
return renewal;
|
|
107
|
+
} catch { return failNetwork(); }
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A later window never moves its slot back and always ends later. Windows need not touch: a
|
|
112
|
+
* receiver that missed renewals while its lease lapsed must still be able to admit the next one.
|
|
113
|
+
*/
|
|
114
|
+
const extendsWindow = (next: IRuntimeNetworkDnsWindow, baseline: IRuntimeNetworkDnsWindow): boolean =>
|
|
115
|
+
next.issuedAt >= baseline.issuedAt && next.expiresAt > baseline.expiresAt;
|
|
116
|
+
|
|
117
|
+
/** A receiver admits only a window it can use at once: its slot has opened and it has not ended. */
|
|
118
|
+
const isCurrentWindow = (window: IRuntimeNetworkDnsWindow, now: number): boolean =>
|
|
119
|
+
window.notBefore <= now && now < window.expiresAt;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The DNS window a node may use under a projection: its admitted renewal's, else the projection's
|
|
123
|
+
* own. Controller and node derive every window-bounded fact, such as a VPN credential's expiry,
|
|
124
|
+
* from this one rule. renewalValue is the bare renewal `admitRuntimeNetworkDnsLeaseRenewal`
|
|
125
|
+
* admitted, or null; Cloudly passes `signed.renewal`. Both inputs are the caller's admitted state,
|
|
126
|
+
* so this checks structure, digests and that the renewal names exactly this projection; it
|
|
127
|
+
* verifies no signature and samples no clock.
|
|
128
|
+
*/
|
|
129
|
+
export const getRuntimeNetworkEffectiveDnsWindow = async (
|
|
130
|
+
projectionValue: unknown, renewalValue: unknown | null,
|
|
131
|
+
): Promise<IRuntimeNetworkDnsWindow> => {
|
|
132
|
+
try {
|
|
133
|
+
const projectionInput = snapshotRuntimeNetworkProjectionV1(projectionValue);
|
|
134
|
+
const renewalInput = renewalValue === null ? null : snapshotRuntimeNetworkDnsLeaseRenewal(renewalValue);
|
|
135
|
+
const projection = await validateRuntimeNetworkProjectionV1(projectionInput);
|
|
136
|
+
const renewal = renewalInput === null ? null : await validateRuntimeNetworkDnsLeaseRenewal(renewalInput);
|
|
137
|
+
return networkEffectiveDnsWindow(projection, renewal);
|
|
138
|
+
} catch { return failNetwork(); }
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Pure admission decision for a renewal whose signature the caller has already verified.
|
|
143
|
+
*
|
|
144
|
+
* currentProjectionValue is the receiver's own admitted projection and previousValue the last
|
|
145
|
+
* renewal it admitted for that exact projection, or null when there is none; neither is ever
|
|
146
|
+
* taken from the incoming message. Without a previous renewal the projection's own window is the
|
|
147
|
+
* baseline. nowValue is the caller's qualified clock: a renewal is admitted only inside its own
|
|
148
|
+
* window, because storing a window that has not opened would displace the coverage the receiver
|
|
149
|
+
* has now. A sender therefore delivers a window once its slot has opened and never before, and
|
|
150
|
+
* simply delivers it again if it was refused. Re-delivering the renewal already admitted is
|
|
151
|
+
* answered `replay` whatever the time, so re-delivery stays idempotent. The caller commits the
|
|
152
|
+
* result atomically with its trust fence and discards the stored renewal in the transaction that
|
|
153
|
+
* admits a successor projection.
|
|
154
|
+
*/
|
|
155
|
+
export const admitRuntimeNetworkDnsLeaseRenewal = async (
|
|
156
|
+
value: unknown, currentProjectionValue: unknown, previousValue: unknown | null, trustedScopeValue: unknown, nowValue: unknown,
|
|
157
|
+
): Promise<{ status: 'accepted' | 'replay'; renewal: IRuntimeNetworkDnsLeaseRenewal }> => {
|
|
158
|
+
try {
|
|
159
|
+
// Snapshot ALL caller-owned inputs before the first asynchronous operation.
|
|
160
|
+
const incoming = snapshotRuntimeNetworkDnsLeaseRenewal(value);
|
|
161
|
+
const projectionInput = snapshotRuntimeNetworkProjectionV1(currentProjectionValue);
|
|
162
|
+
const previousInput = previousValue === null ? null : snapshotRuntimeNetworkDnsLeaseRenewal(previousValue);
|
|
163
|
+
const trusted = snapshotRuntimeNetworkNodeScopeV1(trustedScopeValue);
|
|
164
|
+
const now = networkCounter(nowValue);
|
|
165
|
+
const renewal = await validateRuntimeNetworkDnsLeaseRenewal(incoming);
|
|
166
|
+
const projection = await validateRuntimeNetworkProjectionV1(projectionInput);
|
|
167
|
+
requireNetwork(networkSameScope(projection, trusted) && networkRenewsProjection(renewal, projection));
|
|
168
|
+
const previous = previousInput === null ? null : await validateRuntimeNetworkDnsLeaseRenewal(previousInput);
|
|
169
|
+
if (previous !== null) {
|
|
170
|
+
requireNetwork(networkRenewsProjection(previous, projection));
|
|
171
|
+
if (renewal.sequence === previous.sequence) {
|
|
172
|
+
requireNetwork(networkEqual(renewal, previous)); return { status: 'replay', renewal };
|
|
173
|
+
}
|
|
174
|
+
requireNetwork(renewal.sequence > previous.sequence);
|
|
175
|
+
}
|
|
176
|
+
requireNetwork(isCurrentWindow(renewal.dns, now));
|
|
177
|
+
requireNetwork(extendsWindow(renewal.dns, previous === null ? projection.dns : previous.dns));
|
|
178
|
+
return { status: 'accepted', renewal };
|
|
179
|
+
} catch { return failNetwork(); }
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/** Consecutive held renewals: same signer and scope, a higher sequence, a window that extends the one before. */
|
|
183
|
+
const followsInRun = (next: ISignedRuntimeNetworkDnsLeaseRenewal, before: ISignedRuntimeNetworkDnsLeaseRenewal): boolean =>
|
|
184
|
+
networkEqual(next.authority, before.authority) && networkSameScope(next.renewal, before.renewal)
|
|
185
|
+
&& next.renewal.sequence > before.renewal.sequence && extendsWindow(next.renewal.dns, before.renewal.dns);
|
|
186
|
+
|
|
187
|
+
const heldHorizonMs = (renewals: ISignedRuntimeNetworkDnsLeaseRenewal[]): number => {
|
|
188
|
+
const first = renewals.at(0), last = renewals.at(-1);
|
|
189
|
+
return first === undefined || last === undefined ? 0 : last.renewal.dns.expiresAt - first.renewal.dns.issuedAt;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* A held run is what one node could admit in order: one node, one projection, one authority,
|
|
194
|
+
* ascending sequences, each window extending the one before, inside both escrow maxima.
|
|
195
|
+
*/
|
|
196
|
+
export const snapshotHoldRuntimeNetworkDnsLeaseRenewalsRequest = (value: unknown): IHoldRuntimeNetworkDnsLeaseRenewalsRequest => networkSafe(() => {
|
|
197
|
+
const item = networkRecord(captureNetwork(value), ['nodeId', 'projection', 'renewals']);
|
|
198
|
+
const contract = runtimeNetworkDnsLeaseRenewalContract;
|
|
199
|
+
const request: IHoldRuntimeNetworkDnsLeaseRenewalsRequest = { nodeId: networkId(item.nodeId), projection: networkReference(item.projection),
|
|
200
|
+
renewals: networkList(item.renewals, contract.maximumEscrowedRenewals, snapshotSignedRuntimeNetworkDnsLeaseRenewal) };
|
|
201
|
+
request.renewals.forEach((signed, index) => requireNetwork(signed.renewal.nodeId === request.nodeId
|
|
202
|
+
&& networkEqual(signed.renewal.projection, request.projection)
|
|
203
|
+
&& (index === 0 || followsInRun(signed, request.renewals[index - 1]))));
|
|
204
|
+
requireNetwork(heldHorizonMs(request.renewals) <= contract.maximumEscrowHorizonMs);
|
|
205
|
+
return request;
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
/** The run's rules plus every renewal's digest. It verifies no signature: a relay holds no trust. */
|
|
209
|
+
export const validateHoldRuntimeNetworkDnsLeaseRenewalsRequest = async (value: unknown): Promise<IHoldRuntimeNetworkDnsLeaseRenewalsRequest> => {
|
|
210
|
+
try {
|
|
211
|
+
const request = snapshotHoldRuntimeNetworkDnsLeaseRenewalsRequest(value);
|
|
212
|
+
for (const signed of request.renewals) await validateRuntimeNetworkDnsLeaseRenewal(signed.renewal);
|
|
213
|
+
return request;
|
|
214
|
+
} catch { return failNetwork(); }
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export const snapshotHoldRuntimeNetworkDnsLeaseRenewalsResponse = (value: unknown): IHoldRuntimeNetworkDnsLeaseRenewalsResponse => networkSafe(() => {
|
|
218
|
+
const item = networkRecord(captureNetwork(value), ['held']);
|
|
219
|
+
requireNetwork(typeof item.held === 'number' && Number.isSafeInteger(item.held) && item.held >= 0
|
|
220
|
+
&& item.held <= runtimeNetworkDnsLeaseRenewalContract.maximumEscrowedRenewals);
|
|
221
|
+
return { held: item.held };
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
/** Compare the answer with the exact SENT run: a relay that held fewer did not take the run over. */
|
|
225
|
+
export const bindHoldRuntimeNetworkDnsLeaseRenewalsResponse = (value: unknown, sentRequestValue: unknown): IHoldRuntimeNetworkDnsLeaseRenewalsResponse => networkSafe(() => {
|
|
226
|
+
const response = snapshotHoldRuntimeNetworkDnsLeaseRenewalsResponse(value);
|
|
227
|
+
requireNetwork(response.held === snapshotHoldRuntimeNetworkDnsLeaseRenewalsRequest(sentRequestValue).renewals.length);
|
|
228
|
+
return response;
|
|
229
|
+
});
|
|
@@ -9,10 +9,10 @@ import { routerPacketGrantsOf, snapshotRouterProjection,
|
|
|
9
9
|
type IRuntimeNetworkRouterPacketGrantV1, type IRuntimeNetworkRouterProjectionV1 } from './networkrouter.js';
|
|
10
10
|
import { buildPrivateNetworkFqdnV1, composeServicePrivateNetworkDnsPolicyV1,
|
|
11
11
|
snapshotPrivateNetworkV1, snapshotServicePrivateNetworksV1 } from './privatenetwork.js';
|
|
12
|
-
import { captureNetwork, failNetwork, networkCanonical, networkContains,
|
|
13
|
-
|
|
14
|
-
networkOverlaps, networkPrefixBounds, networkRecord, networkReference, networkReferenceOf, networkRevision,
|
|
15
|
-
|
|
12
|
+
import { captureNetwork, failNetwork, networkCanonical, networkContains, networkCounter, networkDigest,
|
|
13
|
+
networkDnsWindow, networkEqual, networkHash, networkId, networkIpv4, networkIpv4Number, networkList, networkMaximumDnsLeaseMs,
|
|
14
|
+
networkOverlaps, networkPrefixBounds, networkRecord, networkReference, networkReferenceOf, networkRevision, networkSafe,
|
|
15
|
+
networkSameScope, networkScope, networkSorted, requireNetwork } from '../private/networkcontract.js';
|
|
16
16
|
|
|
17
17
|
export interface IRuntimeNetworkNodeScopeV1 {
|
|
18
18
|
controller: IRuntimeControllerV1;
|
|
@@ -20,6 +20,16 @@ export interface IRuntimeNetworkNodeScopeV1 {
|
|
|
20
20
|
runtimeNamespace: string;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* One signed DNS window, on a projection or on a renewal of it: the start of the slot it was
|
|
25
|
+
* issued for, the instant a receiver may use it, and the instant it stops being usable.
|
|
26
|
+
*/
|
|
27
|
+
export interface IRuntimeNetworkDnsWindow {
|
|
28
|
+
issuedAt: number;
|
|
29
|
+
notBefore: number;
|
|
30
|
+
expiresAt: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
23
33
|
/** Immutable per-attempt address material. Membership and projection revisions are independent. */
|
|
24
34
|
export interface IRuntimeNetworkWorkloadLeaseV1 extends IRuntimeNetworkNodeScopeV1 {
|
|
25
35
|
schemaVersion: 1;
|
|
@@ -143,7 +153,7 @@ export interface IRuntimeNetworkProjectionV1 extends IRuntimeNetworkNodeScopeV1
|
|
|
143
153
|
networks: IPrivateNetworkV1[];
|
|
144
154
|
memberships: IServicePrivateNetworksV1[];
|
|
145
155
|
endpoints: IRuntimeNetworkEndpointV1[];
|
|
146
|
-
dns:
|
|
156
|
+
dns: IRuntimeNetworkDnsWindow;
|
|
147
157
|
/** Explicit denial intent. Carry until the exact predecessor's application is proven. */
|
|
148
158
|
withdrawals: IRuntimeNetworkPacketGrantV1[];
|
|
149
159
|
retiredLeases: IRuntimeAuthorityReferenceV1[];
|
|
@@ -173,7 +183,7 @@ export const runtimeNetworkProjectionV1Contract = Object.freeze({
|
|
|
173
183
|
maximumGrants: 4096,
|
|
174
184
|
maximumRouterGrants: 96,
|
|
175
185
|
maximumRouterWithdrawals: 4096,
|
|
176
|
-
maximumDnsLeaseMs:
|
|
186
|
+
maximumDnsLeaseMs: networkMaximumDnsLeaseMs,
|
|
177
187
|
positiveTtlSeconds: 5,
|
|
178
188
|
negativeTtlSeconds: 1,
|
|
179
189
|
omission: 'no-effect-without-explicit-withdrawal',
|
|
@@ -182,11 +192,6 @@ export const runtimeNetworkProjectionV1Contract = Object.freeze({
|
|
|
182
192
|
allocationReuse: 'requires-controller-quarantine-and-all-affected-authority-fences',
|
|
183
193
|
} as const);
|
|
184
194
|
|
|
185
|
-
const scope = (item: Record<string, unknown>): IRuntimeNetworkNodeScopeV1 => ({
|
|
186
|
-
controller: networkController(item.controller), nodeId: networkId(item.nodeId), runtimeNamespace: networkId(item.runtimeNamespace),
|
|
187
|
-
});
|
|
188
|
-
const sameScope = (left: IRuntimeNetworkNodeScopeV1, right: IRuntimeNetworkNodeScopeV1): boolean =>
|
|
189
|
-
networkEqual(left.controller, right.controller) && left.nodeId === right.nodeId && left.runtimeNamespace === right.runtimeNamespace;
|
|
190
195
|
const ownerKey = (value: { organizationId: string; serviceId: string }): string => networkCanonical([value.organizationId, value.serviceId]);
|
|
191
196
|
const attemptKey = (value: IRuntimeNetworkWorkloadLeaseV1): string =>
|
|
192
197
|
networkCanonical([value.organizationId, value.serviceId, value.replicaId, value.attemptId]);
|
|
@@ -195,7 +200,7 @@ const sortedReferences = (value: unknown): IRuntimeAuthorityReferenceV1[] =>
|
|
|
195
200
|
networkSorted(networkList(value, 256, networkReference), refKey);
|
|
196
201
|
|
|
197
202
|
export const snapshotRuntimeNetworkNodeScopeV1 = (value: unknown): IRuntimeNetworkNodeScopeV1 => networkSafe(() =>
|
|
198
|
-
|
|
203
|
+
networkScope(networkRecord(captureNetwork(value), ['controller', 'nodeId', 'runtimeNamespace'])));
|
|
199
204
|
|
|
200
205
|
export const snapshotRuntimeNetworkWorkloadLeaseV1 = (value: unknown): IRuntimeNetworkWorkloadLeaseV1 => networkSafe(() => {
|
|
201
206
|
const item = networkRecord(captureNetwork(value), ['schemaVersion', 'id', 'generation', 'controller', 'nodeId', 'runtimeNamespace',
|
|
@@ -215,7 +220,7 @@ export const snapshotRuntimeNetworkWorkloadLeaseV1 = (value: unknown): IRuntimeN
|
|
|
215
220
|
requireNetwork(number >= low && number <= high && (high - low === 1 || (number !== low && number !== high)));
|
|
216
221
|
}
|
|
217
222
|
requireNetwork(item.dnsServer === gateway);
|
|
218
|
-
return { schemaVersion: 1, id: networkId(item.id), generation: networkCounter(item.generation), ...
|
|
223
|
+
return { schemaVersion: 1, id: networkId(item.id), generation: networkCounter(item.generation), ...networkScope(item),
|
|
219
224
|
organizationId: networkId(item.organizationId), assignmentId: networkId(item.assignmentId), serviceId: networkId(item.serviceId),
|
|
220
225
|
replicaId: networkId(item.replicaId), attemptId: networkId(item.attemptId), protectedAuthority: networkReference(item.protectedAuthority),
|
|
221
226
|
routerIncarnationId: networkId(item.routerIncarnationId), ipv4: { poolId: networkId(ip.poolId), linkSubnet: ip.linkSubnet,
|
|
@@ -312,16 +317,13 @@ export const snapshotRuntimeNetworkProjectionV1 = (value: unknown): IRuntimeNetw
|
|
|
312
317
|
'protectedAuthority', 'allocationAuthorities', 'handoff', 'networks', 'memberships', 'endpoints', 'dns', 'withdrawals', 'retiredLeases', 'retiredHandoffs', 'digest',
|
|
313
318
|
...(hasRouter ? ['router'] : [])]);
|
|
314
319
|
requireNetwork(item.schemaVersion === 1);
|
|
315
|
-
|
|
316
|
-
const window = { issuedAt: networkCounter(dns.issuedAt), notBefore: networkCounter(dns.notBefore), expiresAt: networkCounter(dns.expiresAt) };
|
|
317
|
-
requireNetwork(window.notBefore >= window.issuedAt && window.expiresAt > window.notBefore && window.expiresAt - window.issuedAt <= 900_000);
|
|
318
|
-
return { schemaVersion: 1, ...networkRevision(item), ...scope(item),
|
|
320
|
+
return { schemaVersion: 1, ...networkRevision(item), ...networkScope(item),
|
|
319
321
|
protectedAuthority: snapshotRuntimeNetworkProtectedAuthorityV1(item.protectedAuthority),
|
|
320
322
|
allocationAuthorities: networkSorted(networkList(item.allocationAuthorities, 16, snapshotRuntimeNetworkProtectedAuthorityV1), refKey),
|
|
321
323
|
handoff: item.handoff === null ? null : snapshotRuntimeNetworkHandoffLeaseV1(item.handoff),
|
|
322
324
|
networks: networkSorted(networkList(item.networks, 128, snapshotPrivateNetworkV1), value => value.id),
|
|
323
325
|
memberships: networkSorted(networkList(item.memberships, 256, snapshotServicePrivateNetworksV1), ownerKey),
|
|
324
|
-
endpoints: networkSorted(networkList(item.endpoints, 256, endpoint), value => value.lease.id), dns:
|
|
326
|
+
endpoints: networkSorted(networkList(item.endpoints, 256, endpoint), value => value.lease.id), dns: networkDnsWindow(item.dns),
|
|
325
327
|
withdrawals: networkSorted(networkList(item.withdrawals, 4096, packetGrant), networkCanonical),
|
|
326
328
|
...(hasRouter ? { router: snapshotRouterProjection(item.router) } : {}),
|
|
327
329
|
retiredLeases: sortedReferences(item.retiredLeases), retiredHandoffs: sortedReferences(item.retiredHandoffs), digest: networkDigest(item.digest) };
|
|
@@ -336,14 +338,14 @@ const grantsOf = (projection: IRuntimeNetworkProjectionV1): IRuntimeNetworkPacke
|
|
|
336
338
|
for (const source of projection.endpoints) {
|
|
337
339
|
const member = memberships.get(ownerKey(source.lease)); requireNetwork(member);
|
|
338
340
|
for (const destination of projection.endpoints) {
|
|
339
|
-
if (source === destination || (!
|
|
341
|
+
if (source === destination || (!networkSameScope(source.lease, projection) && !networkSameScope(destination.lease, projection))) continue;
|
|
340
342
|
const target = memberships.get(ownerKey(destination.lease)); requireNetwork(target);
|
|
341
343
|
if (member.organizationId === target.organizationId && member.attachments.some(attachment =>
|
|
342
344
|
target.attachments.some(other => other.networkId === attachment.networkId))) {
|
|
343
345
|
append({ source: networkReferenceOf(source.lease), destination: { kind: 'workload', lease: networkReferenceOf(destination.lease) } });
|
|
344
346
|
}
|
|
345
347
|
}
|
|
346
|
-
if (!
|
|
348
|
+
if (!networkSameScope(source.lease, projection)) continue;
|
|
347
349
|
if (source.publicEgress) append({ source: networkReferenceOf(source.lease), destination: { kind: 'public' } });
|
|
348
350
|
for (const id of source.platformEndpointIds) {
|
|
349
351
|
const platform = projection.protectedAuthority.platformEndpoints.find(item => item.id === id); requireNetwork(platform);
|
|
@@ -373,7 +375,7 @@ export const validateRuntimeNetworkProjectionV1 = async (value: unknown): Promis
|
|
|
373
375
|
};
|
|
374
376
|
if (projection.handoff !== null) {
|
|
375
377
|
await bindRuntimeNetworkHandoffLeaseV1(projection.handoff, allocationAuthority(projection.handoff.protectedAuthority));
|
|
376
|
-
requireNetwork(
|
|
378
|
+
requireNetwork(networkSameScope(projection.handoff, projection));
|
|
377
379
|
requireNetwork(current.pools.some(pool => pool.id === projection.handoff!.transit.poolId && pool.purpose === 'transit'
|
|
378
380
|
&& networkContains(pool.prefix, projection.handoff!.transit.prefix)));
|
|
379
381
|
}
|
|
@@ -413,7 +415,7 @@ export const validateRuntimeNetworkProjectionV1 = async (value: unknown): Promis
|
|
|
413
415
|
&& current.egressAuthorities.some(owner => owner.nodeId === lease.nodeId && owner.runtimeNamespace === lease.runtimeNamespace));
|
|
414
416
|
for (const protection of [authority, current]) requireNetwork(protection.pools.some(pool =>
|
|
415
417
|
pool.id === lease.ipv4.poolId && pool.purpose === 'workload' && networkContains(pool.prefix, lease.ipv4.linkSubnet)));
|
|
416
|
-
if (
|
|
418
|
+
if (networkSameScope(lease, projection)) requireNetwork(projection.handoff !== null && lease.routerIncarnationId === projection.handoff.routerIncarnationId);
|
|
417
419
|
for (const id of endpoint.platformEndpointIds) requireNetwork(current.platformEndpoints.some(platform => platform.id === id));
|
|
418
420
|
for (const port of endpoint.publishedPorts ?? []) {
|
|
419
421
|
const claim = networkCanonical([lease.nodeId, port.protocol, port.hostPort]);
|
|
@@ -450,7 +452,7 @@ export const composeRuntimeNetworkProjectionDnsViewsV1 = async (
|
|
|
450
452
|
): Promise<IRuntimeNetworkDnsViewV1[]> => {
|
|
451
453
|
const selected = sourcesValue === undefined ? null : networkList(captureNetwork(sourcesValue), 256, networkReference);
|
|
452
454
|
const projection = await validateRuntimeNetworkProjectionV1(value);
|
|
453
|
-
const local = projection.endpoints.filter(endpoint =>
|
|
455
|
+
const local = projection.endpoints.filter(endpoint => networkSameScope(endpoint.lease, projection));
|
|
454
456
|
if (selected !== null) {
|
|
455
457
|
requireNetwork(new Set(selected.map(source => source.id)).size === selected.length);
|
|
456
458
|
for (const source of selected) requireNetwork(local.some(endpoint => networkEqual(networkReferenceOf(endpoint.lease), source)));
|
|
@@ -479,7 +481,7 @@ export const composeRuntimeNetworkProjectionDnsViewsV1 = async (
|
|
|
479
481
|
export const bindRuntimeNetworkWorkloadLeaseToAssignmentV1 = async (leaseValue: unknown, assignmentValue: unknown): Promise<IRuntimeNetworkWorkloadLeaseV1> => {
|
|
480
482
|
try {
|
|
481
483
|
const lease = snapshotRuntimeNetworkWorkloadLeaseV1(leaseValue), assignment = snapshotRuntimeAssignmentV1(assignmentValue);
|
|
482
|
-
requireNetwork((await validateRuntimeAssignmentV1(assignment)).length === 0 &&
|
|
484
|
+
requireNetwork((await validateRuntimeAssignmentV1(assignment)).length === 0 && networkSameScope(lease, assignment)
|
|
483
485
|
&& lease.organizationId === assignment.organizationId && lease.assignmentId === assignment.assignmentId
|
|
484
486
|
&& lease.serviceId === assignment.serviceId && lease.replicaId === assignment.replicaId && lease.attemptId === assignment.attemptId
|
|
485
487
|
&& networkEqual(networkReferenceOf(lease), assignment.workload.network)
|
|
@@ -503,7 +505,7 @@ export const admitRuntimeNetworkProjectionV1 = async (
|
|
|
503
505
|
const trusted = snapshotRuntimeNetworkNodeScopeV1(trustedScopeValue);
|
|
504
506
|
const applied = appliedPreviousValue === null ? null : networkReference(captureNetwork(appliedPreviousValue));
|
|
505
507
|
const projection = await validateRuntimeNetworkProjectionV1(incoming);
|
|
506
|
-
requireNetwork(
|
|
508
|
+
requireNetwork(networkSameScope(projection, trusted));
|
|
507
509
|
if (previousInput === null) {
|
|
508
510
|
requireNetwork(projection.generation === 1 && applied === null && projection.withdrawals.length === 0
|
|
509
511
|
&& (projection.router?.withdrawals.length ?? 0) === 0
|
|
@@ -511,7 +513,7 @@ export const admitRuntimeNetworkProjectionV1 = async (
|
|
|
511
513
|
return { status: 'accepted', projection };
|
|
512
514
|
}
|
|
513
515
|
const previous = await validateRuntimeNetworkProjectionV1(previousInput);
|
|
514
|
-
requireNetwork(
|
|
516
|
+
requireNetwork(networkSameScope(previous, trusted) && previous.id === projection.id);
|
|
515
517
|
requireNetwork(applied === null || networkEqual(applied, networkReferenceOf(previous)));
|
|
516
518
|
if (projection.generation === previous.generation) {
|
|
517
519
|
requireNetwork(networkEqual(previous, projection)); return { status: 'replay', projection };
|
|
@@ -3,8 +3,8 @@ import type { IRuntimeAuthorityReferenceV1, IRuntimeControllerV1 } from './runti
|
|
|
3
3
|
import type { IRuntimeNetworkProjectionV1 } from './networkprojection.js';
|
|
4
4
|
import { snapshotRuntimeNetworkProjectionV1 } from './networkprojection.js';
|
|
5
5
|
import { snapshotRuntimeSessionBindingV1 } from './runtimesession.js';
|
|
6
|
-
import { captureNetwork, failNetwork, networkController, networkDigest, networkEqual, networkHash,
|
|
7
|
-
|
|
6
|
+
import { captureNetwork, failNetwork, networkController, networkDigest, networkEqual, networkHash, networkRecord,
|
|
7
|
+
networkReference, networkReferenceOf, networkRevision, networkSafe, networkSignature, requireNetwork } from '../private/networkcontract.js';
|
|
8
8
|
|
|
9
9
|
export interface ISignedRuntimeNetworkProjectionV1 {
|
|
10
10
|
schemaVersion: 1;
|
|
@@ -16,8 +16,9 @@ export interface ISignedRuntimeNetworkProjectionV1 {
|
|
|
16
16
|
|
|
17
17
|
export const snapshotSignedRuntimeNetworkProjectionV1 = (value: unknown): ISignedRuntimeNetworkProjectionV1 => networkSafe(() => {
|
|
18
18
|
const item = networkRecord(captureNetwork(value), ['schemaVersion', 'authority', 'projection', 'signature']);
|
|
19
|
-
requireNetwork(item.schemaVersion === 1
|
|
20
|
-
return { schemaVersion: 1, authority: networkReference(item.authority), projection: snapshotRuntimeNetworkProjectionV1(item.projection),
|
|
19
|
+
requireNetwork(item.schemaVersion === 1);
|
|
20
|
+
return { schemaVersion: 1, authority: networkReference(item.authority), projection: snapshotRuntimeNetworkProjectionV1(item.projection),
|
|
21
|
+
signature: networkSignature(item.signature) };
|
|
21
22
|
});
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { TSha256Digest } from '../data/immutableimage.js';
|
|
2
2
|
import type { IRuntimeAuthorityReferenceV1, IRuntimeControllerV1 } from '../data/runtimeassignment.js';
|
|
3
|
+
import type { IRuntimeNetworkDnsLeaseRenewal } from '../data/networklease.js';
|
|
4
|
+
import type { IRuntimeNetworkDnsWindow, IRuntimeNetworkNodeScopeV1, IRuntimeNetworkProjectionV1 } from '../data/networkprojection.js';
|
|
3
5
|
import { canonicalizeStrictJson, createCanonicalJsonSha256Hex } from './canonicaljson.js';
|
|
4
6
|
|
|
5
7
|
export const failNetwork = (): never => { throw new Error('Runtime network projection contract rejected.'); };
|
|
@@ -83,17 +85,49 @@ export const networkCounter = (value: unknown, maximum = Number.MAX_SAFE_INTEGER
|
|
|
83
85
|
export const networkDigest = (value: unknown): TSha256Digest => {
|
|
84
86
|
requireNetwork(typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value)); return value as TSha256Digest;
|
|
85
87
|
};
|
|
88
|
+
/** One encoding for every Ed25519 signature this contract carries: lowercase hex, 64 bytes. */
|
|
89
|
+
export const networkSignature = (value: unknown): string => {
|
|
90
|
+
requireNetwork(typeof value === 'string' && /^[a-f0-9]{128}$/.test(value)); return value;
|
|
91
|
+
};
|
|
86
92
|
export const networkController = (value: unknown): IRuntimeControllerV1 => {
|
|
87
93
|
const item = networkRecord(value, ['kind', 'id', 'epoch']);
|
|
88
94
|
requireNetwork(item.kind === 'cloudly' || item.kind === 'onebox');
|
|
89
95
|
return { kind: item.kind, id: networkId(item.id), epoch: networkId(item.epoch) };
|
|
90
96
|
};
|
|
97
|
+
export const networkScope = (item: Record<string, unknown>): IRuntimeNetworkNodeScopeV1 => ({
|
|
98
|
+
controller: networkController(item.controller), nodeId: networkId(item.nodeId), runtimeNamespace: networkId(item.runtimeNamespace),
|
|
99
|
+
});
|
|
100
|
+
export const networkSameScope = (left: IRuntimeNetworkNodeScopeV1, right: IRuntimeNetworkNodeScopeV1): boolean =>
|
|
101
|
+
networkEqual(left.controller, right.controller) && left.nodeId === right.nodeId && left.runtimeNamespace === right.runtimeNamespace;
|
|
102
|
+
export const networkMaximumDnsLeaseMs = 900_000;
|
|
103
|
+
/** One rule for every signed DNS window, projection or renewal: ordered, non-empty, at most fifteen minutes. */
|
|
104
|
+
export const networkDnsWindow = (value: unknown): IRuntimeNetworkDnsWindow => {
|
|
105
|
+
const item = networkRecord(value, ['issuedAt', 'notBefore', 'expiresAt']);
|
|
106
|
+
const window = { issuedAt: networkCounter(item.issuedAt), notBefore: networkCounter(item.notBefore), expiresAt: networkCounter(item.expiresAt) };
|
|
107
|
+
requireNetwork(window.notBefore >= window.issuedAt && window.expiresAt > window.notBefore
|
|
108
|
+
&& window.expiresAt - window.issuedAt <= networkMaximumDnsLeaseMs);
|
|
109
|
+
return window;
|
|
110
|
+
};
|
|
91
111
|
export const networkReference = (value: unknown): IRuntimeAuthorityReferenceV1 => {
|
|
92
112
|
const item = networkRecord(value, ['id', 'generation', 'digest']);
|
|
93
113
|
return { id: networkId(item.id), generation: networkCounter(item.generation), digest: networkDigest(item.digest) };
|
|
94
114
|
};
|
|
95
115
|
export const networkReferenceOf = (value: IRuntimeAuthorityReferenceV1): IRuntimeAuthorityReferenceV1 =>
|
|
96
116
|
({ id: value.id, generation: value.generation, digest: value.digest });
|
|
117
|
+
/** A renewal renews exactly one projection: the same node scope and that projection's exact reference. */
|
|
118
|
+
export const networkRenewsProjection = (renewal: IRuntimeNetworkDnsLeaseRenewal, projection: IRuntimeNetworkProjectionV1): boolean =>
|
|
119
|
+
networkSameScope(renewal, projection) && networkEqual(renewal.projection, networkReferenceOf(projection));
|
|
120
|
+
/**
|
|
121
|
+
* The one rule for the DNS window a node may use under a projection: its admitted renewal's, else
|
|
122
|
+
* the projection's own. Both inputs are already validated caller state; this verifies no signature.
|
|
123
|
+
*/
|
|
124
|
+
export const networkEffectiveDnsWindow = (
|
|
125
|
+
projection: IRuntimeNetworkProjectionV1, renewal: IRuntimeNetworkDnsLeaseRenewal | null,
|
|
126
|
+
): IRuntimeNetworkDnsWindow => {
|
|
127
|
+
if (renewal === null) return projection.dns;
|
|
128
|
+
requireNetwork(networkRenewsProjection(renewal, projection));
|
|
129
|
+
return renewal.dns;
|
|
130
|
+
};
|
|
97
131
|
export const networkRevision = (item: Record<string, unknown>) => {
|
|
98
132
|
const id = networkId(item.id), generation = networkCounter(item.generation);
|
|
99
133
|
const previous = item.previous === null ? null : networkReference(item.previous);
|
package/ts/requests/cluster.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as userInterfaces from '../data/user.js';
|
|
2
2
|
import * as clusterInterfaces from '../data/cluster.js';
|
|
3
|
+
import type * as leaseInterfaces from '../data/networklease.js';
|
|
3
4
|
import * as plugins from '../plugins.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -146,6 +147,25 @@ export interface IReq_Relay_Cloudly_RegisterClusterRelayV1 extends plugins.typed
|
|
|
146
147
|
response: clusterInterfaces.IClusterRelayRegistrationResponseV1;
|
|
147
148
|
}
|
|
148
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Cloudly hands the cluster relay the DNS lease renewals it has signed ahead of time for one node.
|
|
152
|
+
*
|
|
153
|
+
* A cluster is outbound-only, so a node that loses Cloudly loses the only signer of its DNS
|
|
154
|
+
* window. The relay holds this run in memory, replacing whatever it held for that node, and
|
|
155
|
+
* releases from it only the renewal whose window is current (`notBefore <= now < expiresAt`), and
|
|
156
|
+
* only while it has no Cloudly session: a node refuses a window that is not current. It signs nothing and verifies
|
|
157
|
+
* nothing: each renewal is Cloudly's statement and the node verifies it against its own pinned
|
|
158
|
+
* authority.
|
|
159
|
+
*/
|
|
160
|
+
export interface IReq_Cloudly_Relay_HoldRuntimeNetworkDnsLeaseRenewals extends plugins.typedrequestInterfaces.implementsTR<
|
|
161
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
162
|
+
IReq_Cloudly_Relay_HoldRuntimeNetworkDnsLeaseRenewals
|
|
163
|
+
> {
|
|
164
|
+
method: 'holdRuntimeNetworkDnsLeaseRenewals';
|
|
165
|
+
request: leaseInterfaces.IHoldRuntimeNetworkDnsLeaseRenewalsRequest;
|
|
166
|
+
response: leaseInterfaces.IHoldRuntimeNetworkDnsLeaseRenewalsResponse;
|
|
167
|
+
}
|
|
168
|
+
|
|
149
169
|
/**
|
|
150
170
|
* deletes a cluster
|
|
151
171
|
*/
|
|
@@ -41,6 +41,23 @@ export interface IReq_Controller_Pallet_ApplyRuntimeNetworkProjectionV1 extends
|
|
|
41
41
|
response: network.IApplyRuntimeNetworkAuthorityResponseV1;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* A fresh DNS window for the node's exact admitted projection; an ACK asserts admission only.
|
|
46
|
+
*
|
|
47
|
+
* The controller sends it while it is reachable. A cluster relay sends the renewals it holds
|
|
48
|
+
* while it has no controller session, under the binding the node obtained itself, which is why
|
|
49
|
+
* a node admits it from whoever holds its current connection and trusts only the signature.
|
|
50
|
+
* Either sender delivers a window once its slot has opened and never before, because a node
|
|
51
|
+
* refuses a renewal it could not use at once; delivering it again once the slot opens is enough.
|
|
52
|
+
*/
|
|
53
|
+
export interface IReq_Controller_Pallet_ApplyRuntimeNetworkDnsLeaseRenewal extends plugins.typedrequestInterfaces.implementsTR<
|
|
54
|
+
plugins.typedrequestInterfaces.ITypedRequest, IReq_Controller_Pallet_ApplyRuntimeNetworkDnsLeaseRenewal
|
|
55
|
+
> {
|
|
56
|
+
method: 'applyRuntimeNetworkDnsLeaseRenewal';
|
|
57
|
+
request: network.IApplyRuntimeNetworkDnsLeaseRenewalRequest;
|
|
58
|
+
response: network.IApplyRuntimeNetworkDnsLeaseRenewalResponse;
|
|
59
|
+
}
|
|
60
|
+
|
|
44
61
|
/**
|
|
45
62
|
* WSS-only Pallet bootstrap. Derive physical peer/idempotency server-side; the registering
|
|
46
63
|
* node process is named by `request.nodeInstanceNonce`, because one relay socket may carry
|