@serve.zone/interfaces 32.41.0 → 32.42.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 +50 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/corestorecontroltoken.d.ts +90 -0
- package/dist_ts/data/corestorecontroltoken.js +106 -0
- package/dist_ts/data/index.d.ts +2 -0
- package/dist_ts/data/index.js +3 -1
- package/dist_ts/data/networkaddressplan.js +3 -12
- package/dist_ts/data/networkauthority.d.ts +16 -0
- package/dist_ts/data/networkauthority.js +45 -1
- package/dist_ts/data/networkcredential.d.ts +12 -8
- package/dist_ts/data/networkcredential.js +18 -13
- package/dist_ts/data/networkegressretirement.d.ts +105 -0
- package/dist_ts/data/networkegressretirement.js +113 -0
- package/dist_ts/data/noderetirement.d.ts +24 -4
- package/dist_ts/data/noderetirement.js +20 -3
- package/dist_ts/private/networkcontract.d.ts +2 -0
- package/dist_ts/private/networkcontract.js +10 -1
- package/dist_ts/requests/corestorecontrol.d.ts +33 -0
- package/dist_ts/requests/corestorecontrol.js +2 -0
- package/dist_ts/requests/index.d.ts +2 -1
- package/dist_ts/requests/index.js +3 -2
- package/dist_ts/requests/node.d.ts +9 -1
- package/package.json +3 -3
- package/readme.md +143 -20
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/corestorecontroltoken.ts +195 -0
- package/ts/data/index.ts +2 -0
- package/ts/data/networkaddressplan.ts +3 -13
- package/ts/data/networkauthority.ts +46 -0
- package/ts/data/networkcredential.ts +17 -12
- package/ts/data/networkegressretirement.ts +195 -0
- package/ts/data/noderetirement.ts +41 -5
- package/ts/private/networkcontract.ts +9 -0
- package/ts/requests/corestorecontrol.ts +43 -0
- package/ts/requests/index.ts +2 -0
- package/ts/requests/node.ts +9 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import type * as userInterfaces from '../data/user.js';
|
|
3
|
+
import type * as controlTokenInterfaces from '../data/corestorecontroltoken.js';
|
|
4
|
+
/**
|
|
5
|
+
* Stores the Corestore control token for every cluster relay to read sealed, so an administrator
|
|
6
|
+
* sets it without an exec into Cloudly's process.
|
|
7
|
+
*
|
|
8
|
+
* One token for both Corestore provider configs, stored in one transaction: either both hold the
|
|
9
|
+
* new token or neither does. `expected` is the state `getCorestoreControlTokenState` answered, and
|
|
10
|
+
* Cloudly stores only while the token is still exactly there; a set that committed in between is
|
|
11
|
+
* refused, never overwritten. Only the verified credential of a freshly authenticated
|
|
12
|
+
* administrator is read, and Cloudly writes the `receipt` in the transaction that stores the token.
|
|
13
|
+
* The token is in this request and nowhere else: never in the answer, a log, a hook or a receipt.
|
|
14
|
+
* Cloudly refuses with one of `data.corestoreControlTokenSetRefusals`.
|
|
15
|
+
*/
|
|
16
|
+
export interface IReq_Admin_Cloudly_SetCorestoreControlToken extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_Cloudly_SetCorestoreControlToken> {
|
|
17
|
+
method: 'setCorestoreControlToken';
|
|
18
|
+
request: controlTokenInterfaces.ICorestoreControlTokenSetRequest & {
|
|
19
|
+
identity: userInterfaces.IIdentityCredential;
|
|
20
|
+
};
|
|
21
|
+
response: controlTokenInterfaces.ICorestoreControlTokenSetResult;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether Cloudly holds the Corestore control token, at which revision, and every receipt of a set,
|
|
25
|
+
* oldest first. Value-free: no token, no hash. Requires an administrator.
|
|
26
|
+
*/
|
|
27
|
+
export interface IReq_Admin_Cloudly_GetCorestoreControlTokenState extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_Cloudly_GetCorestoreControlTokenState> {
|
|
28
|
+
method: 'getCorestoreControlTokenState';
|
|
29
|
+
request: {
|
|
30
|
+
identity: userInterfaces.IIdentityCredential;
|
|
31
|
+
};
|
|
32
|
+
response: controlTokenInterfaces.ICorestoreControlTokenStateRead;
|
|
33
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZXN0b3JlY29udHJvbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3JlcXVlc3RzL2NvcmVzdG9yZWNvbnRyb2wudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUMifQ==
|
|
@@ -10,6 +10,7 @@ import * as configRequests from './config.js';
|
|
|
10
10
|
import * as coreMailRequests from './coremail.js';
|
|
11
11
|
import * as coreMailAdminRequests from './coremail.admin.js';
|
|
12
12
|
import * as corestoreRequests from './corestore.js';
|
|
13
|
+
import * as corestoreControlRequests from './corestorecontrol.js';
|
|
13
14
|
import * as deploymentRequests from './deployment.js';
|
|
14
15
|
import * as dnsRequests from './dns.js';
|
|
15
16
|
import * as domainRequests from './domain.js';
|
|
@@ -36,7 +37,7 @@ import * as statusRequests from './status.js';
|
|
|
36
37
|
import * as taskRequests from './task.js';
|
|
37
38
|
import * as versionRequests from './version.js';
|
|
38
39
|
import * as webPushRequests from './webpush.js';
|
|
39
|
-
export { adminRequests as admin, appStoreRequests as appstore, baremetalRequests as baremetal, baseOsRequests as baseos, backupRequests as backup, certificateRequests as certificate, clusterRequests as cluster, clusterIngressRequests as clusteringress, configRequests as config, coreMailRequests as coremail, coreMailAdminRequests as coremailAdmin, corestoreRequests as corestore, deploymentRequests as deployment, dnsRequests as dns, domainRequests as domain, externalRegistryRequests as externalRegistry, fleetCutoverRequests as fleetcutover, gatewayRequests as gateway, hostedAppRequests as hostedapp, identityRequests as identity, imageRequests as image, informRequests as inform, logRequests as log, mailRequests as mail, migrationRequests as migration, networkRequests as network, nodeRequests as node, palletRequests as pallet, platformRequests as platform, routingRequests as routing, runtimeSessionRequests as runtimesession, secretRequests as secret, serviceRequests as service, settingsRequests as settings, statusRequests as status, taskRequests as task, versionRequests as version, webPushRequests as webpush, };
|
|
40
|
+
export { adminRequests as admin, appStoreRequests as appstore, baremetalRequests as baremetal, baseOsRequests as baseos, backupRequests as backup, certificateRequests as certificate, clusterRequests as cluster, clusterIngressRequests as clusteringress, configRequests as config, coreMailRequests as coremail, coreMailAdminRequests as coremailAdmin, corestoreRequests as corestore, corestoreControlRequests as corestorecontrol, deploymentRequests as deployment, dnsRequests as dns, domainRequests as domain, externalRegistryRequests as externalRegistry, fleetCutoverRequests as fleetcutover, gatewayRequests as gateway, hostedAppRequests as hostedapp, identityRequests as identity, imageRequests as image, informRequests as inform, logRequests as log, mailRequests as mail, migrationRequests as migration, networkRequests as network, nodeRequests as node, palletRequests as pallet, platformRequests as platform, routingRequests as routing, runtimeSessionRequests as runtimesession, secretRequests as secret, serviceRequests as service, settingsRequests as settings, statusRequests as status, taskRequests as task, versionRequests as version, webPushRequests as webpush, };
|
|
40
41
|
export * from './inform.js';
|
|
41
42
|
export * from './hostedapp.js';
|
|
42
43
|
export * from './mail.js';
|
|
@@ -11,6 +11,7 @@ import * as configRequests from './config.js';
|
|
|
11
11
|
import * as coreMailRequests from './coremail.js';
|
|
12
12
|
import * as coreMailAdminRequests from './coremail.admin.js';
|
|
13
13
|
import * as corestoreRequests from './corestore.js';
|
|
14
|
+
import * as corestoreControlRequests from './corestorecontrol.js';
|
|
14
15
|
import * as deploymentRequests from './deployment.js';
|
|
15
16
|
import * as dnsRequests from './dns.js';
|
|
16
17
|
import * as domainRequests from './domain.js';
|
|
@@ -37,9 +38,9 @@ import * as statusRequests from './status.js';
|
|
|
37
38
|
import * as taskRequests from './task.js';
|
|
38
39
|
import * as versionRequests from './version.js';
|
|
39
40
|
import * as webPushRequests from './webpush.js';
|
|
40
|
-
export { adminRequests as admin, appStoreRequests as appstore, baremetalRequests as baremetal, baseOsRequests as baseos, backupRequests as backup, certificateRequests as certificate, clusterRequests as cluster, clusterIngressRequests as clusteringress, configRequests as config, coreMailRequests as coremail, coreMailAdminRequests as coremailAdmin, corestoreRequests as corestore, deploymentRequests as deployment, dnsRequests as dns, domainRequests as domain, externalRegistryRequests as externalRegistry, fleetCutoverRequests as fleetcutover, gatewayRequests as gateway, hostedAppRequests as hostedapp, identityRequests as identity, imageRequests as image, informRequests as inform, logRequests as log, mailRequests as mail, migrationRequests as migration, networkRequests as network, nodeRequests as node, palletRequests as pallet, platformRequests as platform, routingRequests as routing, runtimeSessionRequests as runtimesession, secretRequests as secret, serviceRequests as service, settingsRequests as settings, statusRequests as status, taskRequests as task, versionRequests as version, webPushRequests as webpush, };
|
|
41
|
+
export { adminRequests as admin, appStoreRequests as appstore, baremetalRequests as baremetal, baseOsRequests as baseos, backupRequests as backup, certificateRequests as certificate, clusterRequests as cluster, clusterIngressRequests as clusteringress, configRequests as config, coreMailRequests as coremail, coreMailAdminRequests as coremailAdmin, corestoreRequests as corestore, corestoreControlRequests as corestorecontrol, deploymentRequests as deployment, dnsRequests as dns, domainRequests as domain, externalRegistryRequests as externalRegistry, fleetCutoverRequests as fleetcutover, gatewayRequests as gateway, hostedAppRequests as hostedapp, identityRequests as identity, imageRequests as image, informRequests as inform, logRequests as log, mailRequests as mail, migrationRequests as migration, networkRequests as network, nodeRequests as node, palletRequests as pallet, platformRequests as platform, routingRequests as routing, runtimeSessionRequests as runtimesession, secretRequests as secret, serviceRequests as service, settingsRequests as settings, statusRequests as status, taskRequests as task, versionRequests as version, webPushRequests as webpush, };
|
|
41
42
|
export * from './inform.js';
|
|
42
43
|
export * from './hostedapp.js';
|
|
43
44
|
export * from './mail.js';
|
|
44
45
|
export * from './webpush.js';
|
|
45
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
46
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9yZXF1ZXN0cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUV6QyxPQUFPLEtBQUssYUFBYSxNQUFNLFlBQVksQ0FBQztBQUM1QyxPQUFPLEtBQUssZ0JBQWdCLE1BQU0sZUFBZSxDQUFDO0FBQ2xELE9BQU8sS0FBSyxpQkFBaUIsTUFBTSxnQkFBZ0IsQ0FBQztBQUNwRCxPQUFPLEtBQUssY0FBYyxNQUFNLGFBQWEsQ0FBQztBQUM5QyxPQUFPLEtBQUssY0FBYyxNQUFNLGFBQWEsQ0FBQztBQUM5QyxPQUFPLEtBQUssbUJBQW1CLE1BQU0sa0JBQWtCLENBQUM7QUFDeEQsT0FBTyxLQUFLLGVBQWUsTUFBTSxjQUFjLENBQUM7QUFDaEQsT0FBTyxLQUFLLHNCQUFzQixNQUFNLHFCQUFxQixDQUFDO0FBQzlELE9BQU8sS0FBSyxjQUFjLE1BQU0sYUFBYSxDQUFDO0FBQzlDLE9BQU8sS0FBSyxnQkFBZ0IsTUFBTSxlQUFlLENBQUM7QUFDbEQsT0FBTyxLQUFLLHFCQUFxQixNQUFNLHFCQUFxQixDQUFDO0FBQzdELE9BQU8sS0FBSyxpQkFBaUIsTUFBTSxnQkFBZ0IsQ0FBQztBQUNwRCxPQUFPLEtBQUssd0JBQXdCLE1BQU0sdUJBQXVCLENBQUM7QUFDbEUsT0FBTyxLQUFLLGtCQUFrQixNQUFNLGlCQUFpQixDQUFDO0FBQ3RELE9BQU8sS0FBSyxXQUFXLE1BQU0sVUFBVSxDQUFDO0FBQ3hDLE9BQU8sS0FBSyxjQUFjLE1BQU0sYUFBYSxDQUFDO0FBQzlDLE9BQU8sS0FBSyx3QkFBd0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEtBQUssb0JBQW9CLE1BQU0sbUJBQW1CLENBQUM7QUFDMUQsT0FBTyxLQUFLLGVBQWUsTUFBTSxjQUFjLENBQUM7QUFDaEQsT0FBTyxLQUFLLGlCQUFpQixNQUFNLGdC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
|
|
@@ -7,7 +7,7 @@ import type { IIdentity } from '../data/user.js';
|
|
|
7
7
|
import type { IPlatformObjectStorageEndpoint } from '../platform/objectstorage.js';
|
|
8
8
|
import type { IPlatformDatabaseEndpoint } from '../platform/database.js';
|
|
9
9
|
import type { INodeCredentialMetadata, INodeCredentialMutationPreconditions, INodeCredentialMutationResult, TNodeCredentialPurpose } from '../data/nodecredential.js';
|
|
10
|
-
import type { INodeRuntimeRetirement } from '../data/noderetirement.js';
|
|
10
|
+
import type { INodeRuntimeRetirement, TNodeRuntimeRetirementDisposition } from '../data/noderetirement.js';
|
|
11
11
|
import type { INodeRuntimeBindingRead, INodeRuntimeBindingRequest } from '../data/noderuntimebinding.js';
|
|
12
12
|
import type { INodeEnrollmentRequest, INodeEnrollmentAcknowledgement, INodeEnrollmentState } from '../data/nodeenrollment.js';
|
|
13
13
|
/** Node-self adoption preconditions, not an admin or workload RBAC grant. */
|
|
@@ -175,6 +175,13 @@ export interface IReq_Any_Cloudly_DeleteNodeById extends plugins.typedrequestInt
|
|
|
175
175
|
* `expectedGeneration` is the cluster runtime generation the caller read with `getClusterRuntime`:
|
|
176
176
|
* retirement is a statement about a node of a Pallet cluster, so a cutover that moved the phase
|
|
177
177
|
* meanwhile refuses the write instead of retiring a node out of a runtime it no longer runs in.
|
|
178
|
+
*
|
|
179
|
+
* `disposition` states how the node leaves (`data.TNodeRuntimeRetirementDisposition`). A request
|
|
180
|
+
* that states none asks for `withdrawn`, the retirement this request always described: the node
|
|
181
|
+
* takes part, and nothing is abandoned. `lost` is taken only when stated, is refused as
|
|
182
|
+
* `node-still-reporting` for a node that still reports, and escalates a `withdrawn` retirement that
|
|
183
|
+
* is still `retiring`. Neither disposition shortens the egress fence: the node leaves the protected
|
|
184
|
+
* authority only behind its `data.IRuntimeNetworkEgressRetirement`.
|
|
178
185
|
* Every refusal is one of `data.nodeRuntimeRetirementRefusals` and takes nothing away.
|
|
179
186
|
*/
|
|
180
187
|
export interface IReq_Any_Cloudly_RetireClusterNode extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_RetireClusterNode> {
|
|
@@ -183,6 +190,7 @@ export interface IReq_Any_Cloudly_RetireClusterNode extends plugins.typedrequest
|
|
|
183
190
|
identity: IIdentity;
|
|
184
191
|
nodeId: string;
|
|
185
192
|
expectedGeneration: number;
|
|
193
|
+
disposition?: TNodeRuntimeRetirementDisposition;
|
|
186
194
|
};
|
|
187
195
|
response: {
|
|
188
196
|
retirement: INodeRuntimeRetirement;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/interfaces",
|
|
3
|
-
"version": "32.
|
|
3
|
+
"version": "32.42.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
|
|
6
6
|
"exports": {
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@api.global/typedrequest-interfaces": "^7.1.0",
|
|
25
25
|
"@push.rocks/smartcrypto": "^3.0.0",
|
|
26
|
-
"@push.rocks/smartlog": "^4.0.
|
|
26
|
+
"@push.rocks/smartlog": "^4.0.3",
|
|
27
27
|
"@tsclass/tsclass": "^9.5.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@git.zone/tsbuild": "^5.1.
|
|
30
|
+
"@git.zone/tsbuild": "^5.1.1",
|
|
31
31
|
"@git.zone/tsdoc": "^3.0.1",
|
|
32
32
|
"@git.zone/tsrun": "^3.0.2",
|
|
33
33
|
"@git.zone/tstest": "6.3.2",
|
package/readme.md
CHANGED
|
@@ -395,6 +395,16 @@ the declaration is not a revocation acknowledgement. Prefix arrays are sorted
|
|
|
395
395
|
lexically and nonoverlapping; pools and endpoints are sorted by ID; egress owners
|
|
396
396
|
are ordered by canonical JSON of `[nodeId, runtimeNamespace]`.
|
|
397
397
|
|
|
398
|
+
`continuesRuntimeNetworkProtection(previous, next)` states which successor of a protected authority
|
|
399
|
+
only adds. It answers `true` when `next` is `previous` itself, or exactly the next step of its chain
|
|
400
|
+
(the same `id` and controller, `next.previous` naming `previous`) in which every egress owner, pool,
|
|
401
|
+
resolver and platform endpoint of `previous` is still present with the same content and the prefixes
|
|
402
|
+
still cover every address `previous` protected, however they are split. A cluster topology change is
|
|
403
|
+
such a step: a node joins the egress, or a cluster's hub joins the platform endpoints. The step keeps
|
|
404
|
+
its barrier: every egress owner still reports a receipt for `next` before an allocation binds to it.
|
|
405
|
+
Any removal or change, a jump over a step, another chain or another controller answers `false`. Both
|
|
406
|
+
authorities and their digests are validated, and either that does not validate is rejected.
|
|
407
|
+
|
|
398
408
|
`IRuntimeNetworkProtectionReceipt` reports a worker's durable, joined native
|
|
399
409
|
protection journal separately from projection admission. It binds a complete
|
|
400
410
|
protected-authority reference, node/controller scope, authenticated reporter,
|
|
@@ -411,9 +421,11 @@ current persisted protection receipt and durable session for every declared
|
|
|
411
421
|
egress owner, in canonical owner order. Missing, unavailable, stale-protection
|
|
412
422
|
and superseded-session evidence reject. An offline owner cannot be omitted.
|
|
413
423
|
The controller must fence all those records together with lease insertion, before
|
|
414
|
-
reserving any handoff or workload address.
|
|
415
|
-
|
|
416
|
-
|
|
424
|
+
reserving any handoff or workload address. There is no generic independent-fence
|
|
425
|
+
flag, quorum or expiry: the one way an owner leaves is
|
|
426
|
+
[Egress Owner Retirement](#egress-owner-retirement).
|
|
427
|
+
Protection receipts neither prove workload readiness nor release
|
|
428
|
+
packet/address/name quarantine.
|
|
417
429
|
|
|
418
430
|
`IRuntimeNetworkHandoffLease` binds an immutable node/router incarnation to its
|
|
419
431
|
protected-authority reference, transit subnet and peers, protocol-qualified source
|
|
@@ -732,9 +744,12 @@ A credential names one projection, but its key pair belongs to the node's manage
|
|
|
732
744
|
membership, which outlives it. Every workload add, removal, readiness change or policy change
|
|
733
745
|
signs a new projection generation; none of them changes who the member is.
|
|
734
746
|
`continuesRuntimeManagedVpnMembership(previous, next)` is that rule: `next` continues the
|
|
735
|
-
membership when it is the same projection lineage (`id`) under
|
|
736
|
-
the
|
|
737
|
-
|
|
747
|
+
membership when it is the same projection lineage (`id`), under a protected authority that
|
|
748
|
+
continues the previous one (`continuesRuntimeNetworkProtection`: the same authority or its next
|
|
749
|
+
step that only adds), with the same handoff lease (which names the node scope and carries the
|
|
750
|
+
router incarnation), and is `previous` itself or a later generation. A node joining the egress or a
|
|
751
|
+
cluster hub joining the platform endpoints therefore keeps every member's key pair and hub session.
|
|
752
|
+
A protection step that removes or changes anything or skips a step, a fresh handoff (router
|
|
738
753
|
reincarnation) or a new controller epoch is a new membership; an earlier generation or another
|
|
739
754
|
projection of the same generation continues nothing. It validates both projections and rejects
|
|
740
755
|
one that does not validate.
|
|
@@ -761,6 +776,59 @@ Noise implementation owns cryptographic validation and authentication. Credentia
|
|
|
761
776
|
delivery does not prove a connected VPN, TUN ownership, packet enforcement or
|
|
762
777
|
workload readiness, and does not replace explicit packet withdrawals.
|
|
763
778
|
|
|
779
|
+
### Egress Owner Retirement
|
|
780
|
+
|
|
781
|
+
An owner that is offline, revoked or gone can never acknowledge a successor, so
|
|
782
|
+
while it stays declared no allocation is possible after the next protection change.
|
|
783
|
+
`IRuntimeNetworkEgressRetirement` is the independent fence that lets it leave. Its
|
|
784
|
+
owner is the controller, which records it from its own durable state against the
|
|
785
|
+
exact protected authority the owner is about to leave, once all five hold:
|
|
786
|
+
|
|
787
|
+
1. the node carries an `INodeRuntimeRetirement` row, whose `disposition` the fence
|
|
788
|
+
copies;
|
|
789
|
+
2. both node credentials are revoked (`revokedCredentials`, committed at
|
|
790
|
+
`revokedAt`), so the node can no longer register, renew a DNS lease or fetch a
|
|
791
|
+
managed VPN credential;
|
|
792
|
+
3. every DNS and managed VPN window the node was ever issued has ended
|
|
793
|
+
(`packetWindowExpiresAt`, or `revokedAt` when it was issued none), so no hub
|
|
794
|
+
relays for it even if it was never told to stop, and `fencedAt` is not earlier;
|
|
795
|
+
4. every handoff and workload lease the node was granted is quarantined
|
|
796
|
+
(`quarantinedHandoffs`, at most 256, and `quarantinedWorkloads`, at most 512,
|
|
797
|
+
both in canonical order) and is never handed out again;
|
|
798
|
+
5. every remaining owner of that authority acknowledged a projection head that
|
|
799
|
+
carries no endpoint of the node (`clearedProjections`, one per remaining owner
|
|
800
|
+
in canonical owner order, never the node itself).
|
|
801
|
+
|
|
802
|
+
`lastReceipt` is the node's last accepted protection receipt. A `withdrawn` node
|
|
803
|
+
took part in its own retirement, so it always has one (its null withdrawal); a
|
|
804
|
+
`lost` node may have none. Neither disposition shortens the fence: a lost node
|
|
805
|
+
leaves behind exactly the same controller-owned evidence as a withdrawn one.
|
|
806
|
+
`snapshotRuntimeNetworkEgressRetirement` is the exact-key reader and
|
|
807
|
+
`computeRuntimeNetworkEgressRetirementDigest` seals it under
|
|
808
|
+
`serve.zone/runtime-network-egress-retirement`. Neither authenticates the
|
|
809
|
+
evidence; the controller fences the rows it read together with the authority it
|
|
810
|
+
stages.
|
|
811
|
+
|
|
812
|
+
`bindRuntimeNetworkProtectedAuthorityRetirement(previous, next, fences,
|
|
813
|
+
retiredOwners)` is the pure successor rule for the egress owners and the address
|
|
814
|
+
space of any step. `next` must be the exact successor of `previous` under the same
|
|
815
|
+
controller, with both digests verified. An owner of `previous` may be absent from
|
|
816
|
+
`next` only when exactly one fence recorded against the exact `previous` reference,
|
|
817
|
+
verified by its digest, names it; a fence for an owner `next` still names, or one
|
|
818
|
+
`previous` never named, refuses the step, and every fence's `clearedProjections`
|
|
819
|
+
must name exactly the owners the step keeps. `retiredOwners` is the complete,
|
|
820
|
+
canonically ordered list of owners earlier steps fenced, which the caller's
|
|
821
|
+
durable history supplies; neither side may name any of them, so a fenced owner
|
|
822
|
+
never returns. Every pool of `previous` stays unchanged and the prefixes of `next`
|
|
823
|
+
cover those of `previous`; a step that removes an owner also keeps every resolver.
|
|
824
|
+
Owners may join in the same step, and a step without fences is the additive rule.
|
|
825
|
+
One step consumes at most 16 fences (`runtimeNetworkEgressRetirementContract`).
|
|
826
|
+
|
|
827
|
+
A retirement step is a new protected authority: every remaining owner reports a
|
|
828
|
+
receipt citing it before anything new is allocated, while existing leases keep
|
|
829
|
+
their historical eligibility. It also ends every managed VPN membership, as every
|
|
830
|
+
protection change does, so each retirement costs the fleet one reconnect.
|
|
831
|
+
|
|
764
832
|
### DNS Lease Renewals
|
|
765
833
|
|
|
766
834
|
A projection's `dns` window lasts at most fifteen minutes and is signed content, so a node's
|
|
@@ -1822,20 +1890,33 @@ status or assignments changed; it carries no state, so a late push never overwri
|
|
|
1822
1890
|
|
|
1823
1891
|
### Node Retirement
|
|
1824
1892
|
|
|
1825
|
-
`data.INodeRuntimeRetirement { id, clusterId, state, actorId, since }` records that one
|
|
1826
|
-
its cluster's Pallet runtime. `TNodeRuntimeRetirementState` is `retiring` while the
|
|
1827
|
-
stops and removes the node's slots, the compiler quarantines its handoff and
|
|
1828
|
-
are revoked
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
`
|
|
1838
|
-
|
|
1893
|
+
`data.INodeRuntimeRetirement { id, clusterId, state, disposition, actorId, since }` records that one
|
|
1894
|
+
node leaves its cluster's Pallet runtime. `TNodeRuntimeRetirementState` is `retiring` while the
|
|
1895
|
+
producer still stops and removes the node's slots, the compiler quarantines its handoff and leases,
|
|
1896
|
+
both node credentials are revoked and the node's egress fence is not yet recorded, and `retired`
|
|
1897
|
+
once the protected authority no longer names the node (see
|
|
1898
|
+
[Egress Owner Retirement](#egress-owner-retirement)). It is stored beside the node document, because
|
|
1899
|
+
a node update merges caller data and no caller may retire a node with it. The producers read the
|
|
1900
|
+
row's presence rather than its state: a node that has one is no longer an egress member and desires
|
|
1901
|
+
no slots, so the row is written before anything is taken away and stays as long as the node
|
|
1902
|
+
document. `snapshotNodeRuntimeRetirement` checks the record.
|
|
1903
|
+
|
|
1904
|
+
`TNodeRuntimeRetirementDisposition` (`nodeRuntimeRetirementDispositions`,
|
|
1905
|
+
`snapshotNodeRuntimeRetirementDisposition`) says how the node leaves. `withdrawn`: the node still
|
|
1906
|
+
runs and takes part — its slots are removed against its own removal receipts and its Pallet
|
|
1907
|
+
withdraws its protection receipt before its credentials are revoked. `lost`: an administrator
|
|
1908
|
+
states the node will never report again, which authorizes abandoning its slots without removal
|
|
1909
|
+
receipts and nothing else; its egress fence is the same as a withdrawn node's.
|
|
1910
|
+
|
|
1911
|
+
`requests.node.retireClusterNode { identity, nodeId, expectedGeneration, disposition? }` answers
|
|
1912
|
+
`{ retirement }`. `expectedGeneration` is the cluster runtime generation the caller read with
|
|
1913
|
+
`getClusterRuntime`, so a cutover that moved the phase meanwhile refuses the write. A request that
|
|
1914
|
+
states no disposition asks for `withdrawn`, the retirement this request always described; `lost`
|
|
1915
|
+
is taken only when stated, and asking again as `lost` escalates a `withdrawn` retirement that is
|
|
1916
|
+
still `retiring`. Every refusal is one of `data.nodeRuntimeRetirementRefusals` — `node-missing`,
|
|
1917
|
+
`cluster-runtime-not-pallet`, `node-already-retired`, `conflict` or `node-still-reporting` (a `lost`
|
|
1918
|
+
retirement of a node whose last accepted report is younger than
|
|
1919
|
+
`clusterNodeObservationFreshnessMs`) — and takes nothing away.
|
|
1839
1920
|
|
|
1840
1921
|
### Corestore Database Export Packaging
|
|
1841
1922
|
|
|
@@ -3611,6 +3692,7 @@ Request groups are exported by product area:
|
|
|
3611
3692
|
- `requests.coremail`
|
|
3612
3693
|
- `requests.coremailAdmin`
|
|
3613
3694
|
- `requests.corestore`
|
|
3695
|
+
- `requests.corestorecontrol`
|
|
3614
3696
|
- `requests.deployment`
|
|
3615
3697
|
- `requests.dns`
|
|
3616
3698
|
- `requests.domain`
|
|
@@ -3724,6 +3806,47 @@ bytes and digests exactly.
|
|
|
3724
3806
|
payload, one-byte-larger framed snapshot-original, 128 MiB verified closure
|
|
3725
3807
|
plaintext, and 256 MiB closure boundaries used by Corestore.
|
|
3726
3808
|
|
|
3809
|
+
### Corestore Control Token Administration
|
|
3810
|
+
|
|
3811
|
+
The elected relay of every cluster calls its nodes' Corestore control API with one control token
|
|
3812
|
+
(`CORESTORE_API_TOKEN`), which Cloudly stores for both Corestore provider configs and hands to the
|
|
3813
|
+
relay sealed (see Corestore Runtime Credentials). `requests.corestorecontrol` lets an administrator
|
|
3814
|
+
store that token without an exec into Cloudly's process.
|
|
3815
|
+
|
|
3816
|
+
`setCorestoreControlToken { identity, token, expected }` stores `token` under both provider configs
|
|
3817
|
+
in one transaction, so either both hold it or neither does. `token` must pass
|
|
3818
|
+
`isCorestoreControlToken` (32 to 4096 UTF-8 bytes, no whitespace) and is the only member of this
|
|
3819
|
+
contract that carries the token: it is never in an answer, a receipt, a log or a hook, and a sender
|
|
3820
|
+
never persists the request. `expected` is the `data.TCorestoreControlTokenState` the administrator
|
|
3821
|
+
last read: `{ state: 'absent' }` or `{ state: 'present', revision }`, never a minimum, so a set that
|
|
3822
|
+
committed in between is refused rather than overwritten. `revision` is 1 for the first token ever
|
|
3823
|
+
stored and advances by exactly one with every accepted set; no request removes a token. It requires
|
|
3824
|
+
a freshly authenticated administrator, exactly as a relay credential mutation does. The answer,
|
|
3825
|
+
`data.ICorestoreControlTokenSetResult`, is the state the set produced and its receipt. Refusals are
|
|
3826
|
+
`data.corestoreControlTokenSetRefusals` (`data.TCorestoreControlTokenSetRefusal`):
|
|
3827
|
+
`token-invalid`, `token-present`, `token-absent`, `token-revision-mismatch` and
|
|
3828
|
+
`provider-unavailable`.
|
|
3829
|
+
|
|
3830
|
+
Every accepted set is audited: Cloudly writes `data.ICorestoreControlTokenSetReceipt` (`id`,
|
|
3831
|
+
`revision`, `via`, `actorId`, `setAt`) in the transaction that stores the token. `via` is
|
|
3832
|
+
`admin-request` with the administrator's `actorId`, or `container-cli` with
|
|
3833
|
+
`data.corestoreControlTokenContainerCliActorId` (`operator:secrets-cli`) when Cloudly's in-container
|
|
3834
|
+
`secrets set-corestore-control-token` stored it; that command advances the same revision. A receipt
|
|
3835
|
+
is value-free: no token, no hash. `getCorestoreControlTokenState { identity }` answers
|
|
3836
|
+
`data.ICorestoreControlTokenStateRead`: the state and its receipts, oldest first, contiguous and
|
|
3837
|
+
ending at the state's revision; no token stored means no receipt. Only a token stored before
|
|
3838
|
+
receipts existed, which Cloudly's migration records as revision 1, has no receipt, so the receipts
|
|
3839
|
+
start at revision 1 or 2. A read answers at most `data.corestoreControlTokenMaximumReceipts` (4096)
|
|
3840
|
+
receipts: past that many sets it answers the latest 4096, whose oldest may name any revision.
|
|
3841
|
+
|
|
3842
|
+
A reader checks a set answer with `bindCorestoreControlTokenSet(answer, request)`: the state is
|
|
3843
|
+
present at revision 1 after an absent expectation and at the expected revision plus one otherwise,
|
|
3844
|
+
and the receipt is an `admin-request` receipt for exactly that revision.
|
|
3845
|
+
`snapshotCorestoreControlTokenSetRequest`, `snapshotCorestoreControlTokenSetResult`,
|
|
3846
|
+
`snapshotCorestoreControlTokenSetReceipt`, `snapshotCorestoreControlTokenState` and
|
|
3847
|
+
`snapshotCorestoreControlTokenStateRead` are the exact-keyed readers. Every refusal is the
|
|
3848
|
+
contract's one value-free message and never quotes the token.
|
|
3849
|
+
|
|
3727
3850
|
### Corestore Through The Relay
|
|
3728
3851
|
|
|
3729
3852
|
A serve.zone cluster is outbound-only and Corestore is node-local, so Cloudly never reaches a
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { isCorestoreControlToken } from '../runtime.corestore.js';
|
|
2
|
+
import {
|
|
3
|
+
captureNetwork,
|
|
4
|
+
networkCounter,
|
|
5
|
+
networkId,
|
|
6
|
+
networkList,
|
|
7
|
+
networkRecord,
|
|
8
|
+
requireNetwork,
|
|
9
|
+
} from '../private/networkcontract.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Whether Cloudly holds the Corestore control token, and at which revision. Value-free.
|
|
13
|
+
*
|
|
14
|
+
* There is one token for both Corestore provider configs, so this is one state rather than one per
|
|
15
|
+
* provider. `revision` is 1 for the first token ever stored and advances by exactly one with every
|
|
16
|
+
* accepted set, whichever path set it. No request removes a token, so a present token never turns
|
|
17
|
+
* absent again.
|
|
18
|
+
*/
|
|
19
|
+
export type TCorestoreControlTokenState =
|
|
20
|
+
| { state: 'absent' }
|
|
21
|
+
| { state: 'present'; revision: number };
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* What an administrator names to store the Corestore control token: the token, and the state the
|
|
25
|
+
* administrator last read. Never a minimum: a set that committed after that read is refused rather
|
|
26
|
+
* than overwritten.
|
|
27
|
+
*
|
|
28
|
+
* `token` is the secret itself, the only member of this contract that carries it. A sender never
|
|
29
|
+
* logs, hooks or persists the request; Cloudly never answers or records the token.
|
|
30
|
+
*/
|
|
31
|
+
export interface ICorestoreControlTokenSetRequest {
|
|
32
|
+
token: string;
|
|
33
|
+
expected: TCorestoreControlTokenState;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Which path stored a token: an administrator's typed request, or the `secrets
|
|
38
|
+
* set-corestore-control-token` command run inside Cloudly's container.
|
|
39
|
+
*/
|
|
40
|
+
export type TCorestoreControlTokenSetPath = 'admin-request' | 'container-cli';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The audit record of one accepted set, which Cloudly writes in the transaction that stores the
|
|
44
|
+
* token. Append-only and value-free: it names which revision was stored, by whom, how and when,
|
|
45
|
+
* never the token or a hash of it.
|
|
46
|
+
*/
|
|
47
|
+
export interface ICorestoreControlTokenSetReceipt {
|
|
48
|
+
id: string;
|
|
49
|
+
/** The revision this set stored. */
|
|
50
|
+
revision: number;
|
|
51
|
+
via: TCorestoreControlTokenSetPath;
|
|
52
|
+
/** The administrator read from the verified credential, or `operator:secrets-cli`. */
|
|
53
|
+
actorId: string;
|
|
54
|
+
setAt: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** What Cloudly answers to an accepted set: the state it produced and its receipt. Value-free. */
|
|
58
|
+
export interface ICorestoreControlTokenSetResult {
|
|
59
|
+
state: { state: 'present'; revision: number };
|
|
60
|
+
receipt: ICorestoreControlTokenSetReceipt;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The token's state and its most recent receipts, oldest first: every receipt while the token has
|
|
65
|
+
* been set at most `corestoreControlTokenMaximumReceipts` times, the latest that many after. Value-free.
|
|
66
|
+
*/
|
|
67
|
+
export interface ICorestoreControlTokenStateRead {
|
|
68
|
+
state: TCorestoreControlTokenState;
|
|
69
|
+
receipts: ICorestoreControlTokenSetReceipt[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Every reason Cloudly answers instead of storing a Corestore control token. */
|
|
73
|
+
export const corestoreControlTokenSetRefusals = Object.freeze([
|
|
74
|
+
/** The token is outside the contract: its length, encoding or whitespace (`isCorestoreControlToken`). */
|
|
75
|
+
'token-invalid',
|
|
76
|
+
/** `expected` said absent, and a token is stored. */
|
|
77
|
+
'token-present',
|
|
78
|
+
/** `expected` named a revision, and no token is stored. */
|
|
79
|
+
'token-absent',
|
|
80
|
+
/** `expected` named a revision, and the stored token is at another one. */
|
|
81
|
+
'token-revision-mismatch',
|
|
82
|
+
/** A Corestore provider config is missing or disabled, so there is nothing to store it under. */
|
|
83
|
+
'provider-unavailable',
|
|
84
|
+
] as const);
|
|
85
|
+
|
|
86
|
+
export type TCorestoreControlTokenSetRefusal = typeof corestoreControlTokenSetRefusals[number];
|
|
87
|
+
|
|
88
|
+
/** The actor a receipt names when the in-container command stored the token. */
|
|
89
|
+
export const corestoreControlTokenContainerCliActorId = 'operator:secrets-cli' as const;
|
|
90
|
+
|
|
91
|
+
/** The most receipts one state read answers: the latest ones, once a token has been set more often. */
|
|
92
|
+
export const corestoreControlTokenMaximumReceipts = 4096;
|
|
93
|
+
|
|
94
|
+
const fail = (): never => { throw new Error('Corestore control token contract rejected.'); };
|
|
95
|
+
const safe = <T>(operationArg: () => T): T => {
|
|
96
|
+
try { return operationArg(); } catch { return fail(); }
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const readState = (valueArg: unknown): TCorestoreControlTokenState => {
|
|
100
|
+
const value = networkRecord(valueArg, ['state'], ['revision']);
|
|
101
|
+
if (value.state === 'absent') {
|
|
102
|
+
networkRecord(value, ['state']);
|
|
103
|
+
return { state: 'absent' };
|
|
104
|
+
}
|
|
105
|
+
requireNetwork(value.state === 'present');
|
|
106
|
+
return { state: 'present', revision: networkCounter(networkRecord(value, ['state', 'revision']).revision) };
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export const snapshotCorestoreControlTokenState = (
|
|
110
|
+
valueArg: unknown,
|
|
111
|
+
): TCorestoreControlTokenState => safe(() => readState(captureNetwork(valueArg)));
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Structural copy of a set request. A refusal never quotes the request, because it carries the
|
|
115
|
+
* token: every failure is the contract's one value-free message.
|
|
116
|
+
*/
|
|
117
|
+
export const snapshotCorestoreControlTokenSetRequest = (
|
|
118
|
+
valueArg: unknown,
|
|
119
|
+
): ICorestoreControlTokenSetRequest => safe(() => {
|
|
120
|
+
const value = networkRecord(captureNetwork(valueArg), ['token', 'expected']);
|
|
121
|
+
requireNetwork(isCorestoreControlToken(value.token));
|
|
122
|
+
return { token: value.token, expected: readState(value.expected) };
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const readReceipt = (valueArg: unknown): ICorestoreControlTokenSetReceipt => {
|
|
126
|
+
const value = networkRecord(valueArg, ['id', 'revision', 'via', 'actorId', 'setAt']);
|
|
127
|
+
requireNetwork(value.via === 'admin-request' || value.via === 'container-cli');
|
|
128
|
+
const actorId = networkId(value.actorId);
|
|
129
|
+
requireNetwork(value.via === 'admin-request'
|
|
130
|
+
? actorId !== corestoreControlTokenContainerCliActorId
|
|
131
|
+
: actorId === corestoreControlTokenContainerCliActorId);
|
|
132
|
+
return {
|
|
133
|
+
id: networkId(value.id),
|
|
134
|
+
revision: networkCounter(value.revision),
|
|
135
|
+
via: value.via,
|
|
136
|
+
actorId,
|
|
137
|
+
setAt: networkCounter(value.setAt),
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const snapshotCorestoreControlTokenSetReceipt = (
|
|
142
|
+
valueArg: unknown,
|
|
143
|
+
): ICorestoreControlTokenSetReceipt => safe(() => readReceipt(captureNetwork(valueArg)));
|
|
144
|
+
|
|
145
|
+
export const snapshotCorestoreControlTokenSetResult = (
|
|
146
|
+
valueArg: unknown,
|
|
147
|
+
): ICorestoreControlTokenSetResult => safe(() => {
|
|
148
|
+
const value = networkRecord(captureNetwork(valueArg), ['state', 'receipt']);
|
|
149
|
+
const state = readState(value.state);
|
|
150
|
+
requireNetwork(state.state === 'present');
|
|
151
|
+
return { state, receipt: readReceipt(value.receipt) };
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Structural copy of a state read. No token stored means no receipt. A stored token's receipts are
|
|
156
|
+
* contiguous and end at its revision, and they start at revision 1 -- or at 2, because only a token
|
|
157
|
+
* stored before receipts existed reaches revision 1 without one -- unless the read is a full window
|
|
158
|
+
* of `corestoreControlTokenMaximumReceipts`, whose oldest receipt may name any later revision.
|
|
159
|
+
*/
|
|
160
|
+
export const snapshotCorestoreControlTokenStateRead = (
|
|
161
|
+
valueArg: unknown,
|
|
162
|
+
): ICorestoreControlTokenStateRead => safe(() => {
|
|
163
|
+
const value = networkRecord(captureNetwork(valueArg), ['state', 'receipts']);
|
|
164
|
+
const state = readState(value.state);
|
|
165
|
+
const receipts = networkList(value.receipts, corestoreControlTokenMaximumReceipts, readReceipt);
|
|
166
|
+
if (state.state === 'absent') {
|
|
167
|
+
requireNetwork(receipts.length === 0);
|
|
168
|
+
return { state, receipts };
|
|
169
|
+
}
|
|
170
|
+
const first = state.revision - receipts.length + 1;
|
|
171
|
+
requireNetwork(first >= 1
|
|
172
|
+
&& (first <= 2 || receipts.length === corestoreControlTokenMaximumReceipts)
|
|
173
|
+
&& receipts.every((receipt, index) => receipt.revision === first + index));
|
|
174
|
+
return { state, receipts };
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Joins a set answer to the request it answers. The answered state is the one the request
|
|
179
|
+
* produces -- revision 1 when no token was stored, the expected revision plus one otherwise -- and
|
|
180
|
+
* the receipt is an administrator's receipt for exactly that revision. Content join only: whether
|
|
181
|
+
* the token was stored is Cloudly's to prove, and the answer never carries it.
|
|
182
|
+
*/
|
|
183
|
+
export const bindCorestoreControlTokenSet = (
|
|
184
|
+
resultArg: unknown,
|
|
185
|
+
requestArg: unknown,
|
|
186
|
+
): ICorestoreControlTokenSetResult => safe(() => {
|
|
187
|
+
const request = snapshotCorestoreControlTokenSetRequest(requestArg);
|
|
188
|
+
const result = snapshotCorestoreControlTokenSetResult(resultArg);
|
|
189
|
+
const produced = request.expected.state === 'absent' ? 1 : request.expected.revision + 1;
|
|
190
|
+
requireNetwork(Number.isSafeInteger(produced)
|
|
191
|
+
&& result.state.revision === produced
|
|
192
|
+
&& result.receipt.revision === produced
|
|
193
|
+
&& result.receipt.via === 'admin-request');
|
|
194
|
+
return result;
|
|
195
|
+
});
|
package/ts/data/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './clusteringressdesignation.js';
|
|
|
7
7
|
export * from './clusterrelaycredential.js';
|
|
8
8
|
export * from './config.js';
|
|
9
9
|
export * from './corestore.js';
|
|
10
|
+
export * from './corestorecontroltoken.js';
|
|
10
11
|
export * from './corestorelegacyadoption.js';
|
|
11
12
|
export * from './coremail.js';
|
|
12
13
|
export * from './coremail.runtime.js';
|
|
@@ -38,6 +39,7 @@ export * from './networkaddressplan.js';
|
|
|
38
39
|
export * from './networkauthority.js';
|
|
39
40
|
export * from './networkcredential.js';
|
|
40
41
|
export * from './networkdelivery.js';
|
|
42
|
+
export * from './networkegressretirement.js';
|
|
41
43
|
export * from './networklease.js';
|
|
42
44
|
export * from './networknode.js';
|
|
43
45
|
export * from './networkprojection.js';
|
|
@@ -2,8 +2,8 @@ import type { TSha256Digest } from './immutableimage.js';
|
|
|
2
2
|
import type { IRuntimeNetworkProtectedAuthority } from './networkauthority.js';
|
|
3
3
|
import { computeRuntimeNetworkProtectedAuthorityDigest, runtimeNetworkAuthorityContract,
|
|
4
4
|
snapshotRuntimeNetworkProtectedAuthority } from './networkauthority.js';
|
|
5
|
-
import { captureNetwork, failNetwork, networkContains, networkCounter, networkEqual, networkId, networkIpv4, networkList,
|
|
6
|
-
networkOverlaps, networkPrefix,
|
|
5
|
+
import { captureNetwork, failNetwork, networkContains, networkCounter, networkCoveredBy, networkEqual, networkId, networkIpv4, networkList,
|
|
6
|
+
networkOverlaps, networkPrefix, networkPrivatePrefix, networkRecord, networkSafe,
|
|
7
7
|
requireNetwork, RuntimeNetworkAddressPlanStructureError, type TRuntimeNetworkAddressPlanOrderedField,
|
|
8
8
|
} from '../private/networkcontract.js';
|
|
9
9
|
|
|
@@ -279,16 +279,6 @@ export const composeRuntimeNetworkProtectedAuthority = async (
|
|
|
279
279
|
} catch { return failNetwork(); }
|
|
280
280
|
};
|
|
281
281
|
|
|
282
|
-
/** Whether the union of `prefixes`, which never overlap one another, covers every address of `prefix`. */
|
|
283
|
-
const coveredBy = (prefixes: string[], prefix: string): boolean => {
|
|
284
|
-
const [first, last] = networkPrefixBounds(prefix);
|
|
285
|
-
const covered = prefixes.reduce((sum, candidate) => {
|
|
286
|
-
const [low, high] = networkPrefixBounds(candidate);
|
|
287
|
-
return sum + Math.max(0, Math.min(last, high) - Math.max(first, low) + 1);
|
|
288
|
-
}, 0);
|
|
289
|
-
return covered === last - first + 1;
|
|
290
|
-
};
|
|
291
|
-
|
|
292
282
|
/**
|
|
293
283
|
* The entries of `previous` that `next` does not hold exactly as they were. An entry changed under its
|
|
294
284
|
* old id counts: whatever still names that id would now reach something else.
|
|
@@ -314,7 +304,7 @@ export const admitRuntimeNetworkAddressPlanChange = (previousValue: unknown | nu
|
|
|
314
304
|
if (networkEqual({ ...previous, revision: next.revision }, next)) return { status: 'unchanged' };
|
|
315
305
|
if (!networkEqual(previous.vpn, next.vpn)) return { status: 'refused', reason: 'plan-vpn-immutable' };
|
|
316
306
|
if (removedOrChanged(previous.pools, next.pools).length > 0) return { status: 'refused', reason: 'plan-pool-removed' };
|
|
317
|
-
if (!previous.prefixes.every(prefix =>
|
|
307
|
+
if (!previous.prefixes.every(prefix => networkCoveredBy(next.prefixes, prefix))) return { status: 'refused', reason: 'plan-prefix-shrunk' };
|
|
318
308
|
return { status: 'accepted', removed: { resolverIds: idsOf(removedOrChanged(previous.resolvers, next.resolvers)),
|
|
319
309
|
platformEndpointIds: idsOf(removedOrChanged(previous.platformEndpoints, next.platformEndpoints)) } };
|
|
320
310
|
});
|