@serve.zone/interfaces 31.8.0 → 32.0.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/.smartconfig.json +1 -0
- package/changelog.md +512 -0
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/appstore/types.d.ts +4 -6
- package/dist_ts/appstore/types.js +5 -5
- package/dist_ts/data/backup.d.ts +3 -26
- package/dist_ts/data/cluster.d.ts +36 -11
- package/dist_ts/data/cluster.js +33 -22
- package/dist_ts/data/clusteringress.d.ts +8 -0
- package/dist_ts/data/clusteringress.js +13 -8
- package/dist_ts/data/clusternode.d.ts +121 -74
- package/dist_ts/data/clusternode.js +109 -132
- package/dist_ts/data/clusterruntime.d.ts +39 -0
- package/dist_ts/data/clusterruntime.js +11 -0
- package/dist_ts/data/coremail.d.ts +3 -7
- package/dist_ts/data/coremail.js +2 -2
- package/dist_ts/data/coremail.runtime.d.ts +0 -1
- package/dist_ts/data/coremail.runtime.js +5 -22
- package/dist_ts/data/deploymentadoption.d.ts +0 -2
- package/dist_ts/data/deploymentadoption.js +40 -6
- package/dist_ts/data/deploymentoperation.js +16 -3
- package/dist_ts/data/deploymentpreflight.d.ts +0 -2
- package/dist_ts/data/deploymentpreflight.js +1 -1
- package/dist_ts/data/immutableimage.d.ts +38 -21
- package/dist_ts/data/immutableimage.js +45 -5
- package/dist_ts/data/index.d.ts +5 -1
- package/dist_ts/data/index.js +5 -1
- package/dist_ts/data/isolatedrestore.d.ts +139 -21
- package/dist_ts/data/isolatedrestore.golden.d.ts +0 -1
- package/dist_ts/data/isolatedrestore.golden.js +6 -9
- package/dist_ts/data/isolatedrestore.js +176 -40
- package/dist_ts/data/mail.d.ts +1 -1
- package/dist_ts/data/networkaddressplan.d.ts +78 -0
- package/dist_ts/data/networkaddressplan.js +114 -0
- package/dist_ts/data/networkauthority.d.ts +20 -22
- package/dist_ts/data/networkauthority.js +29 -43
- package/dist_ts/data/networkcredential.d.ts +10 -10
- package/dist_ts/data/networkcredential.js +16 -16
- package/dist_ts/data/networkdelivery.d.ts +18 -18
- package/dist_ts/data/networkdelivery.js +16 -16
- package/dist_ts/data/networklease.d.ts +18 -7
- package/dist_ts/data/networklease.js +29 -7
- package/dist_ts/data/networknode.d.ts +37 -0
- package/dist_ts/data/networknode.js +6 -0
- package/dist_ts/data/networkprojection.d.ts +59 -60
- package/dist_ts/data/networkprojection.js +53 -57
- package/dist_ts/data/networkprotection.d.ts +27 -28
- package/dist_ts/data/networkprotection.js +36 -36
- package/dist_ts/data/networkrouter.d.ts +11 -11
- package/dist_ts/data/networkrouter.js +1 -1
- package/dist_ts/data/networksigning.d.ts +16 -18
- package/dist_ts/data/networksigning.js +20 -21
- package/dist_ts/data/nodecredential.d.ts +4 -6
- package/dist_ts/data/nodeenrollment.d.ts +35 -40
- package/dist_ts/data/nodeenrollment.js +57 -72
- package/dist_ts/data/privatenetwork.d.ts +21 -24
- package/dist_ts/data/privatenetwork.js +39 -54
- package/dist_ts/data/runtimeassignment.d.ts +50 -34
- package/dist_ts/data/runtimeassignment.js +79 -45
- package/dist_ts/data/runtimeconfig.d.ts +21 -22
- package/dist_ts/data/runtimeconfig.js +37 -100
- package/dist_ts/data/runtimesession.d.ts +100 -62
- package/dist_ts/data/runtimesession.js +98 -79
- package/dist_ts/data/runtimeterminalreceipt.d.ts +28 -29
- package/dist_ts/data/runtimeterminalreceipt.js +42 -42
- package/dist_ts/data/secret.d.ts +18 -25
- package/dist_ts/data/secret.js +12 -52
- package/dist_ts/data/servicecredential.d.ts +14 -16
- package/dist_ts/data/servicecredential.js +17 -28
- package/dist_ts/data/serviceruntime.d.ts +128 -0
- package/dist_ts/data/serviceruntime.js +92 -0
- package/dist_ts/data/sparknodereport.d.ts +27 -14
- package/dist_ts/data/sparknodereport.js +35 -28
- package/dist_ts/data/webpush.d.ts +0 -1
- package/dist_ts/index.d.ts +2 -1
- package/dist_ts/index.js +3 -2
- package/dist_ts/platform/objectstorageretention.d.ts +21 -24
- package/dist_ts/platform/objectstorageretention.js +6 -18
- package/dist_ts/platform/storage.d.ts +1 -3
- package/dist_ts/platform/storagemigration.d.ts +0 -7
- package/dist_ts/platform/storagemigration.golden.d.ts +0 -1
- package/dist_ts/platform/storagemigration.golden.js +8 -12
- package/dist_ts/platform/storagemigration.js +6 -51
- package/dist_ts/platform/types.d.ts +2 -2
- package/dist_ts/private/canonicaljson.d.ts +9 -1
- package/dist_ts/private/canonicaljson.js +14 -2
- package/dist_ts/private/dataproperties.d.ts +62 -0
- package/dist_ts/private/dataproperties.js +117 -0
- package/dist_ts/private/networkcontract.d.ts +14 -10
- package/dist_ts/private/networkcontract.js +15 -1
- package/dist_ts/private/producercontract.d.ts +11 -0
- package/dist_ts/private/producercontract.js +21 -0
- package/dist_ts/private/releaseversion.d.ts +37 -0
- package/dist_ts/private/releaseversion.js +48 -0
- package/dist_ts/private/runtimepolicy.d.ts +31 -0
- package/dist_ts/private/runtimepolicy.js +90 -0
- package/dist_ts/private/tokenlist.d.ts +9 -0
- package/dist_ts/private/tokenlist.js +23 -0
- package/dist_ts/protocol.d.ts +137 -0
- package/dist_ts/protocol.js +255 -0
- package/dist_ts/requests/admin.d.ts +8 -8
- package/dist_ts/requests/backup.d.ts +102 -112
- package/dist_ts/requests/cluster.d.ts +47 -10
- package/dist_ts/requests/cluster.js +1 -1
- package/dist_ts/requests/clusteringress.d.ts +9 -0
- package/dist_ts/requests/clusteringress.js +1 -1
- package/dist_ts/requests/config.d.ts +3 -3
- package/dist_ts/requests/corestore.d.ts +0 -3
- package/dist_ts/requests/corestore.js +5 -9
- package/dist_ts/requests/network.d.ts +87 -31
- package/dist_ts/requests/node.d.ts +15 -28
- package/dist_ts/requests/pallet.d.ts +13 -13
- package/dist_ts/requests/runtimesession.d.ts +53 -41
- package/dist_ts/requests/secret.d.ts +4 -5
- package/dist_ts/requests/secret.js +1 -5
- package/dist_ts/requests/service.d.ts +60 -0
- package/dist_ts/runtime.corestore.d.ts +56 -74
- package/dist_ts/runtime.corestore.databaseexportpackage.d.ts +30 -36
- package/dist_ts/runtime.corestore.databaseexportpackage.golden.d.ts +11 -12
- package/dist_ts/runtime.corestore.databaseexportpackage.golden.js +54 -71
- package/dist_ts/runtime.corestore.databaseexportpackage.js +9 -25
- package/dist_ts/runtime.corestore.js +35 -135
- package/dist_ts/runtime.corestore.relay.js +2 -7
- package/dist_ts/runtime.d.ts +38 -48
- package/dist_ts/runtime.js +20 -77
- package/dist_ts/runtime.networkprojection.d.ts +4 -4
- package/dist_ts/runtime.networkprojection.js +18 -18
- package/dist_ts/runtime.workloadinit.d.ts +23 -24
- package/dist_ts/runtime.workloadinit.js +6 -13
- package/package.json +2 -2
- package/readme.md +540 -269
- package/ts/00_commitinfo_data.ts +3 -3
- package/ts/appstore/types.ts +4 -6
- package/ts/data/backup.ts +3 -38
- package/ts/data/cluster.ts +51 -34
- package/ts/data/clusteringress.ts +12 -12
- package/ts/data/clusternode.ts +204 -232
- package/ts/data/clusterruntime.ts +45 -0
- package/ts/data/coremail.runtime.ts +4 -21
- package/ts/data/coremail.ts +3 -7
- package/ts/data/deploymentadoption.ts +46 -4
- package/ts/data/deploymentoperation.ts +16 -1
- package/ts/data/deploymentpreflight.ts +0 -2
- package/ts/data/immutableimage.ts +71 -24
- package/ts/data/index.ts +5 -1
- package/ts/data/isolatedrestore.golden.ts +5 -9
- package/ts/data/isolatedrestore.ts +339 -54
- package/ts/data/mail.ts +1 -1
- package/ts/data/networkaddressplan.ts +175 -0
- package/ts/data/networkauthority.ts +45 -61
- package/ts/data/networkcredential.ts +24 -24
- package/ts/data/networkdelivery.ts +27 -27
- package/ts/data/networklease.ts +40 -13
- package/ts/data/networknode.ts +37 -0
- package/ts/data/networkprojection.ts +119 -124
- package/ts/data/networkprotection.ts +54 -55
- package/ts/data/networkrouter.ts +14 -14
- package/ts/data/networksigning.ts +29 -32
- package/ts/data/nodecredential.ts +4 -6
- package/ts/data/nodeenrollment.ts +96 -114
- package/ts/data/privatenetwork.ts +58 -74
- package/ts/data/runtimeassignment.ts +143 -94
- package/ts/data/runtimeconfig.ts +62 -122
- package/ts/data/runtimesession.ts +185 -138
- package/ts/data/runtimeterminalreceipt.ts +72 -73
- package/ts/data/secret.ts +28 -76
- package/ts/data/servicecredential.ts +33 -43
- package/ts/data/serviceruntime.ts +208 -0
- package/ts/data/sparknodereport.ts +48 -36
- package/ts/data/webpush.ts +0 -1
- package/ts/index.ts +2 -0
- package/ts/platform/objectstorageretention.ts +36 -55
- package/ts/platform/storage.ts +1 -3
- package/ts/platform/storagemigration.golden.ts +7 -11
- package/ts/platform/storagemigration.ts +5 -55
- package/ts/platform/types.ts +2 -2
- package/ts/private/canonicaljson.ts +13 -1
- package/ts/private/dataproperties.ts +139 -0
- package/ts/private/networkcontract.ts +23 -9
- package/ts/private/producercontract.ts +19 -0
- package/ts/private/releaseversion.ts +70 -0
- package/ts/private/runtimepolicy.ts +105 -0
- package/ts/private/tokenlist.ts +30 -0
- package/ts/protocol.ts +364 -0
- package/ts/requests/admin.ts +11 -11
- package/ts/requests/backup.ts +112 -117
- package/ts/requests/cluster.ts +58 -13
- package/ts/requests/clusteringress.ts +9 -0
- package/ts/requests/config.ts +3 -3
- package/ts/requests/corestore.ts +4 -16
- package/ts/requests/network.ts +90 -41
- package/ts/requests/node.ts +21 -38
- package/ts/requests/pallet.ts +17 -17
- package/ts/requests/runtimesession.ts +63 -51
- package/ts/requests/secret.ts +5 -10
- package/ts/requests/service.ts +81 -0
- package/ts/runtime.corestore.databaseexportpackage.golden.ts +77 -95
- package/ts/runtime.corestore.databaseexportpackage.ts +51 -73
- package/ts/runtime.corestore.relay.ts +1 -10
- package/ts/runtime.corestore.ts +118 -268
- package/ts/runtime.networkprojection.ts +30 -30
- package/ts/runtime.ts +83 -158
- package/ts/runtime.workloadinit.ts +36 -48
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.d.ts +0 -902
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.d.ts +0 -35
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.js +0 -315
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.js +0 -3315
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.d.ts +0 -291
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.d.ts +0 -30
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.js +0 -408
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.js +0 -1126
- package/ts/runtime.cloudlylegacydeploymentsettlement.golden.ts +0 -421
- package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.ts +0 -510
- package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.ts +0 -2470
- package/ts/runtime.cloudlylegacydeploymentsettlement.ts +0 -6630
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { TSha256Digest } from './immutableimage.js';
|
|
2
|
+
import type { IRuntimeNetworkProtectedAuthority } from './networkauthority.js';
|
|
3
|
+
import { computeRuntimeNetworkProtectedAuthorityDigest, snapshotRuntimeNetworkProtectedAuthority } from './networkauthority.js';
|
|
4
|
+
import type { IGetRuntimeManagedVpnCredentialResponse } from './networkcredential.js';
|
|
5
|
+
import { captureNetwork, failNetwork, networkContains, networkCounter, networkEqual, networkId, networkIpv4, networkOverlaps,
|
|
6
|
+
networkPrefixBounds, networkPrivatePrefix, networkRecord, networkSafe, requireNetwork } from '../private/networkcontract.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The address space an administrator gives the runtime network, and the one input the protected
|
|
10
|
+
* authority is composed from. Cloudly keeps a single plan; pools and prefixes only grow, and the
|
|
11
|
+
* managed VPN never changes once set.
|
|
12
|
+
*/
|
|
13
|
+
export interface IRuntimeNetworkAddressPlan {
|
|
14
|
+
id: 'address-plan';
|
|
15
|
+
/** Compare-and-swap counter; every accepted change advances it by one. */
|
|
16
|
+
revision: number;
|
|
17
|
+
/** Every protected prefix: they cover the pools, resolvers, platform endpoints and the VPN. */
|
|
18
|
+
prefixes: string[];
|
|
19
|
+
pools: IRuntimeNetworkProtectedAuthority['pools'];
|
|
20
|
+
resolvers: IRuntimeNetworkProtectedAuthority['resolvers'];
|
|
21
|
+
/** Without the VPN relay, which the composed authority adds as a UDP platform endpoint. */
|
|
22
|
+
platformEndpoints: IRuntimeNetworkProtectedAuthority['platformEndpoints'];
|
|
23
|
+
vpn: {
|
|
24
|
+
/** The private prefix VPN control addresses are allocated from. */
|
|
25
|
+
controlPrefix: string;
|
|
26
|
+
/** The endpoint every node dials for the managed VPN. */
|
|
27
|
+
relay: IGetRuntimeManagedVpnCredentialResponse['relay'];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** What an administrator writes; Cloudly supplies the id and the revision. */
|
|
32
|
+
export type TRuntimeNetworkAddressPlanWritableData = Omit<IRuntimeNetworkAddressPlan, 'id' | 'revision'>;
|
|
33
|
+
|
|
34
|
+
/** What a protection producer fixes around a plan: the authority's identity, revision chain and egress. */
|
|
35
|
+
export type TRuntimeNetworkProtectedAuthorityFrame = Pick<IRuntimeNetworkProtectedAuthority,
|
|
36
|
+
'id' | 'generation' | 'controller' | 'previous' | 'egressAuthorities'>;
|
|
37
|
+
|
|
38
|
+
/** Refusals the pure change check decides from the two plans alone. */
|
|
39
|
+
export type TRuntimeNetworkAddressPlanChangeRefusal = 'plan-vpn-immutable' | 'plan-pool-removed' | 'plan-prefix-shrunk';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Every refusal of a plan write. Cloudly adds the two that need live references: a resolver or
|
|
43
|
+
* platform endpoint the change removes or changes while a router selection or a service still names it.
|
|
44
|
+
*/
|
|
45
|
+
export type TRuntimeNetworkAddressPlanRefusal =
|
|
46
|
+
| TRuntimeNetworkAddressPlanChangeRefusal | 'plan-resolver-referenced' | 'plan-endpoint-referenced';
|
|
47
|
+
|
|
48
|
+
export type TRuntimeNetworkAddressPlanChange =
|
|
49
|
+
| { status: 'unchanged' }
|
|
50
|
+
/** The ids the change removes or changes, which Cloudly checks against live references before it writes. */
|
|
51
|
+
| { status: 'accepted'; removed: { resolverIds: string[]; platformEndpointIds: string[] } }
|
|
52
|
+
| { status: 'refused'; reason: TRuntimeNetworkAddressPlanChangeRefusal };
|
|
53
|
+
|
|
54
|
+
export const runtimeNetworkAddressPlanContract = Object.freeze({
|
|
55
|
+
id: 'address-plan',
|
|
56
|
+
/**
|
|
57
|
+
* SmartVPN needs its gateway and at least one member inside the control prefix. No minimum length
|
|
58
|
+
* is stated: the private-range rule already keeps the control prefix at /8 or longer.
|
|
59
|
+
*/
|
|
60
|
+
maximumControlPrefixLength: 30,
|
|
61
|
+
relayProtocol: 'udp',
|
|
62
|
+
} as const);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The structural authority validator reads a frame's shape and nothing else, and a plan fixes none of
|
|
66
|
+
* the frame, so every valid frame decides a plan the same way. The digest is a well-formed
|
|
67
|
+
* placeholder: this candidate is never hashed or stored.
|
|
68
|
+
*/
|
|
69
|
+
const validationFrame: TRuntimeNetworkProtectedAuthorityFrame = {
|
|
70
|
+
id: 'address-plan', generation: 1, controller: { kind: 'cloudly', id: 'address-plan', epoch: 'address-plan' },
|
|
71
|
+
previous: null, egressAuthorities: [{ nodeId: 'address-plan', runtimeNamespace: 'address-plan' }],
|
|
72
|
+
};
|
|
73
|
+
const unsealedDigest = `sha256:${'0'.repeat(64)}` as TSha256Digest;
|
|
74
|
+
|
|
75
|
+
type TAuthoritySets = Pick<IRuntimeNetworkProtectedAuthority, 'prefixes' | 'pools' | 'resolvers' | 'platformEndpoints'>;
|
|
76
|
+
|
|
77
|
+
/** Unvalidated sets and frame in, the protected authority validator's verdict out. */
|
|
78
|
+
const authorityOf = (
|
|
79
|
+
sets: Record<keyof TAuthoritySets, unknown>, frame: Record<keyof TRuntimeNetworkProtectedAuthorityFrame, unknown>,
|
|
80
|
+
): IRuntimeNetworkProtectedAuthority => snapshotRuntimeNetworkProtectedAuthority({ id: frame.id,
|
|
81
|
+
generation: frame.generation, controller: frame.controller, previous: frame.previous, egressAuthorities: frame.egressAuthorities,
|
|
82
|
+
prefixes: sets.prefixes, pools: sets.pools, resolvers: sets.resolvers, platformEndpoints: sets.platformEndpoints, digest: unsealedDigest });
|
|
83
|
+
|
|
84
|
+
const setsWithRelay = (plan: IRuntimeNetworkAddressPlan): TAuthoritySets => ({
|
|
85
|
+
prefixes: plan.prefixes, pools: plan.pools, resolvers: plan.resolvers,
|
|
86
|
+
platformEndpoints: [...plan.platformEndpoints, { ...plan.vpn.relay, protocol: runtimeNetworkAddressPlanContract.relayProtocol }]
|
|
87
|
+
.sort((left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const vpnOf = (value: unknown): IRuntimeNetworkAddressPlan['vpn'] => {
|
|
91
|
+
const item = networkRecord(value, ['controlPrefix', 'relay']);
|
|
92
|
+
const relay = networkRecord(item.relay, ['id', 'address', 'port']);
|
|
93
|
+
const controlPrefix = networkPrivatePrefix(item.controlPrefix);
|
|
94
|
+
requireNetwork(Number(controlPrefix.split('/')[1]) <= runtimeNetworkAddressPlanContract.maximumControlPrefixLength);
|
|
95
|
+
return { controlPrefix, relay: { id: networkId(relay.id), address: networkIpv4(relay.address), port: networkCounter(relay.port, 65535) } };
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** The control prefix is protected, and no pool, resolver or platform endpoint, the relay included, sits inside it. */
|
|
99
|
+
const requireVpnProtected = (authority: IRuntimeNetworkProtectedAuthority, controlPrefix: string): void => {
|
|
100
|
+
requireNetwork(authority.prefixes.some(prefix => networkContains(prefix, controlPrefix))
|
|
101
|
+
&& authority.pools.every(pool => !networkOverlaps(pool.prefix, controlPrefix))
|
|
102
|
+
&& [...authority.resolvers, ...authority.platformEndpoints].every(endpoint => !networkContains(controlPrefix, `${endpoint.address}/32`)));
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Structural snapshot for exact persistence and for a plan write. A plan passes only when both
|
|
107
|
+
* authorities it can describe pass the protected authority's own validator: its sets alone, and
|
|
108
|
+
* its sets with the relay added, so no plan can compose an authority that contract refuses.
|
|
109
|
+
*/
|
|
110
|
+
export const snapshotRuntimeNetworkAddressPlan = (value: unknown): IRuntimeNetworkAddressPlan => networkSafe(() => {
|
|
111
|
+
const item = networkRecord(captureNetwork(value), ['id', 'revision', 'prefixes', 'pools', 'resolvers', 'platformEndpoints', 'vpn']);
|
|
112
|
+
requireNetwork(item.id === runtimeNetworkAddressPlanContract.id);
|
|
113
|
+
const own = authorityOf({ prefixes: item.prefixes, pools: item.pools, resolvers: item.resolvers,
|
|
114
|
+
platformEndpoints: item.platformEndpoints }, validationFrame);
|
|
115
|
+
const plan: IRuntimeNetworkAddressPlan = { id: runtimeNetworkAddressPlanContract.id, revision: networkCounter(item.revision),
|
|
116
|
+
prefixes: own.prefixes, pools: own.pools, resolvers: own.resolvers, platformEndpoints: own.platformEndpoints, vpn: vpnOf(item.vpn) };
|
|
117
|
+
requireVpnProtected(authorityOf(setsWithRelay(plan), validationFrame), plan.vpn.controlPrefix);
|
|
118
|
+
return plan;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The protected authority a plan describes inside a producer's frame, digest included: the plan's
|
|
123
|
+
* sets with the VPN relay as the UDP platform endpoint every node dials. Both inputs are read before
|
|
124
|
+
* the digest is computed.
|
|
125
|
+
*/
|
|
126
|
+
export const composeRuntimeNetworkProtectedAuthority = async (
|
|
127
|
+
planValue: unknown, frameValue: unknown,
|
|
128
|
+
): Promise<IRuntimeNetworkProtectedAuthority> => {
|
|
129
|
+
try {
|
|
130
|
+
const plan = snapshotRuntimeNetworkAddressPlan(planValue);
|
|
131
|
+
const frame = networkRecord(captureNetwork(frameValue), ['id', 'generation', 'controller', 'previous', 'egressAuthorities']);
|
|
132
|
+
const authority = authorityOf(setsWithRelay(plan), frame);
|
|
133
|
+
return { ...authority, digest: await computeRuntimeNetworkProtectedAuthorityDigest(authority) };
|
|
134
|
+
} catch { return failNetwork(); }
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/** Whether the union of `prefixes`, which never overlap one another, covers every address of `prefix`. */
|
|
138
|
+
const coveredBy = (prefixes: string[], prefix: string): boolean => {
|
|
139
|
+
const [first, last] = networkPrefixBounds(prefix);
|
|
140
|
+
const covered = prefixes.reduce((sum, candidate) => {
|
|
141
|
+
const [low, high] = networkPrefixBounds(candidate);
|
|
142
|
+
return sum + Math.max(0, Math.min(last, high) - Math.max(first, low) + 1);
|
|
143
|
+
}, 0);
|
|
144
|
+
return covered === last - first + 1;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The entries of `previous` that `next` does not hold exactly as they were. An entry changed under its
|
|
149
|
+
* old id counts: whatever still names that id would now reach something else.
|
|
150
|
+
*/
|
|
151
|
+
const removedOrChanged = <T>(previous: T[], next: T[]): T[] =>
|
|
152
|
+
previous.filter(item => !next.some(candidate => networkEqual(candidate, item)));
|
|
153
|
+
|
|
154
|
+
const idsOf = (entries: Array<{ id: string }>): string[] => entries.map(entry => entry.id);
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The pure decision for replacing `previousValue`, the stored plan or null, with `nextValue` at the
|
|
158
|
+
* following revision. Leases and the managed VPN outlive a plan revision, so every pool stays exactly
|
|
159
|
+
* as it was, the protected address space only grows, and the VPN never changes. Resolvers and platform
|
|
160
|
+
* endpoints may go or change; `accepted` names every one that did. Equal content is `unchanged`:
|
|
161
|
+
* Cloudly writes nothing and the stored revision stands.
|
|
162
|
+
*/
|
|
163
|
+
export const admitRuntimeNetworkAddressPlanChange = (previousValue: unknown | null, nextValue: unknown): TRuntimeNetworkAddressPlanChange =>
|
|
164
|
+
networkSafe(() => {
|
|
165
|
+
const previous = previousValue === null ? null : snapshotRuntimeNetworkAddressPlan(previousValue);
|
|
166
|
+
const next = snapshotRuntimeNetworkAddressPlan(nextValue);
|
|
167
|
+
requireNetwork(next.revision === (previous?.revision ?? 0) + 1);
|
|
168
|
+
if (previous === null) return { status: 'accepted', removed: { resolverIds: [], platformEndpointIds: [] } };
|
|
169
|
+
if (networkEqual({ ...previous, revision: next.revision }, next)) return { status: 'unchanged' };
|
|
170
|
+
if (!networkEqual(previous.vpn, next.vpn)) return { status: 'refused', reason: 'plan-vpn-immutable' };
|
|
171
|
+
if (removedOrChanged(previous.pools, next.pools).length > 0) return { status: 'refused', reason: 'plan-pool-removed' };
|
|
172
|
+
if (!previous.prefixes.every(prefix => coveredBy(next.prefixes, prefix))) return { status: 'refused', reason: 'plan-prefix-shrunk' };
|
|
173
|
+
return { status: 'accepted', removed: { resolverIds: idsOf(removedOrChanged(previous.resolvers, next.resolvers)),
|
|
174
|
+
platformEndpointIds: idsOf(removedOrChanged(previous.platformEndpoints, next.platformEndpoints)) } };
|
|
175
|
+
});
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { TSha256Digest } from './immutableimage.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { IRuntimeAuthorityReference, IRuntimeController } from './runtimeassignment.js';
|
|
3
3
|
import { canonicalizeStrictJson, createCanonicalJsonSha256Hex } from '../private/canonicaljson.js';
|
|
4
|
+
import { networkPrefix, networkPrivatePrefix } from '../private/networkcontract.js';
|
|
4
5
|
|
|
5
6
|
/** Controller declaration; a digest does not authenticate completeness or activate a pool. */
|
|
6
|
-
export interface
|
|
7
|
-
schemaVersion: 1;
|
|
7
|
+
export interface IRuntimeNetworkProtectedAuthority {
|
|
8
8
|
id: string;
|
|
9
9
|
generation: number;
|
|
10
|
-
controller:
|
|
11
|
-
previous:
|
|
10
|
+
controller: IRuntimeController;
|
|
11
|
+
previous: IRuntimeAuthorityReference | null;
|
|
12
12
|
/** Complete canonical IPv4 union, including all pools and protected endpoints. */
|
|
13
13
|
prefixes: string[];
|
|
14
14
|
pools: Array<{ id: string; purpose: 'workload' | 'transit'; prefix: string }>;
|
|
@@ -20,33 +20,32 @@ export interface IRuntimeNetworkProtectedAuthorityV1 {
|
|
|
20
20
|
digest: TSha256Digest;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export interface
|
|
23
|
+
export interface IRuntimeNetworkHandoffPortRange {
|
|
24
24
|
protocol: 'tcp' | 'udp';
|
|
25
25
|
first: number;
|
|
26
26
|
last: number;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/** Immutable allocation, retained unchanged while active or quarantined. No expiry implies reuse. */
|
|
30
|
-
export interface
|
|
31
|
-
schemaVersion: 1;
|
|
30
|
+
export interface IRuntimeNetworkHandoffLease {
|
|
32
31
|
id: string;
|
|
33
32
|
generation: number;
|
|
34
|
-
controller:
|
|
33
|
+
controller: IRuntimeController;
|
|
35
34
|
nodeId: string;
|
|
36
35
|
runtimeNamespace: string;
|
|
37
|
-
protectedAuthority:
|
|
36
|
+
protectedAuthority: IRuntimeAuthorityReference;
|
|
38
37
|
/** Logical allocation incarnation, never a path, PID, inode, descriptor or native capability. */
|
|
39
38
|
routerIncarnationId: string;
|
|
40
39
|
transit: { poolId: string; prefix: string; routerAddress: string; hostAddress: string };
|
|
41
|
-
sourcePortRanges:
|
|
40
|
+
sourcePortRanges: IRuntimeNetworkHandoffPortRange[];
|
|
42
41
|
conntrackZone: number;
|
|
43
42
|
conntrackLabel: string;
|
|
44
43
|
digest: TSha256Digest;
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
export const
|
|
48
|
-
protectedDigestDomain: 'serve.zone/runtime-network-protected-authority
|
|
49
|
-
handoffDigestDomain: 'serve.zone/runtime-network-handoff-lease
|
|
46
|
+
export const runtimeNetworkAuthorityContract = Object.freeze({
|
|
47
|
+
protectedDigestDomain: 'serve.zone/runtime-network-protected-authority',
|
|
48
|
+
handoffDigestDomain: 'serve.zone/runtime-network-handoff-lease',
|
|
50
49
|
maximumPrefixes: 128,
|
|
51
50
|
maximumPools: 64,
|
|
52
51
|
maximumResolvers: 16,
|
|
@@ -100,12 +99,12 @@ const digest = (value: unknown): TSha256Digest => {
|
|
|
100
99
|
requireValue(typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value));
|
|
101
100
|
return value as TSha256Digest;
|
|
102
101
|
};
|
|
103
|
-
const controller = (value: unknown):
|
|
102
|
+
const controller = (value: unknown): IRuntimeController => {
|
|
104
103
|
const item = record(value, ['kind', 'id', 'epoch']);
|
|
105
104
|
requireValue(item.kind === 'cloudly' || item.kind === 'onebox');
|
|
106
105
|
return { kind: item.kind, id: id(item.id), epoch: id(item.epoch) };
|
|
107
106
|
};
|
|
108
|
-
const reference = (value: unknown):
|
|
107
|
+
const reference = (value: unknown): IRuntimeAuthorityReference => {
|
|
109
108
|
const item = record(value, ['id', 'generation', 'digest']);
|
|
110
109
|
return { id: id(item.id), generation: integer(item.generation), digest: digest(item.digest) };
|
|
111
110
|
};
|
|
@@ -129,14 +128,6 @@ const endpointAddress = (value: unknown): string => {
|
|
|
129
128
|
requireValue(first > 0 && first < 224 && first !== 127);
|
|
130
129
|
return result;
|
|
131
130
|
};
|
|
132
|
-
const prefix = (value: unknown): string => {
|
|
133
|
-
requireValue(typeof value === 'string' && value.length <= 18);
|
|
134
|
-
const parts = value.split('/');
|
|
135
|
-
requireValue(parts.length === 2 && /^(?:[0-9]|[12][0-9]|3[0-2])$/.test(parts[1]));
|
|
136
|
-
const base = numericAddress(address(parts[0])), width = 2 ** (32 - Number(parts[1]));
|
|
137
|
-
requireValue(base % width === 0);
|
|
138
|
-
return value;
|
|
139
|
-
};
|
|
140
131
|
const bounds = (value: string): [number, number] => {
|
|
141
132
|
const [base, bits] = value.split('/'), first = numericAddress(base);
|
|
142
133
|
return [first, first + 2 ** (32 - Number(bits)) - 1];
|
|
@@ -149,11 +140,6 @@ const overlaps = (left: string, right: string): boolean => {
|
|
|
149
140
|
const [a, b] = bounds(left), [c, d] = bounds(right);
|
|
150
141
|
return a <= d && c <= b;
|
|
151
142
|
};
|
|
152
|
-
const privatePrefix = (value: unknown): string => {
|
|
153
|
-
const result = prefix(value);
|
|
154
|
-
requireValue(['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'].some(range => contains(range, result)));
|
|
155
|
-
return result;
|
|
156
|
-
};
|
|
157
143
|
const nonoverlapping = (values: string[]): void => {
|
|
158
144
|
for (let index = 0; index < values.length; index++) for (let other = 0; other < index; other++) {
|
|
159
145
|
requireValue(!overlaps(values[index], values[other]));
|
|
@@ -163,22 +149,21 @@ const canonical = (value: unknown): string => canonicalizeStrictJson(value, fail
|
|
|
163
149
|
const equals = (left: unknown, right: unknown): boolean => canonical(left) === canonical(right);
|
|
164
150
|
|
|
165
151
|
/** Structural snapshot only. Complete inventory, signatures and activation barriers are caller-owned. */
|
|
166
|
-
export const
|
|
167
|
-
const item = record(value, ['
|
|
152
|
+
export const snapshotRuntimeNetworkProtectedAuthority = (value: unknown): IRuntimeNetworkProtectedAuthority => safe(() => {
|
|
153
|
+
const item = record(value, ['id', 'generation', 'controller', 'previous', 'prefixes',
|
|
168
154
|
'pools', 'resolvers', 'platformEndpoints', 'egressAuthorities', 'digest']);
|
|
169
|
-
requireValue(item.schemaVersion === 1);
|
|
170
155
|
const identity = id(item.id), generation = integer(item.generation);
|
|
171
156
|
const previous = item.previous === null ? null : reference(item.previous);
|
|
172
157
|
requireValue(generation === 1 ? previous === null : previous?.id === identity && previous.generation === generation - 1);
|
|
173
|
-
const prefixes = sorted(list(item.prefixes,
|
|
158
|
+
const prefixes = sorted(list(item.prefixes, runtimeNetworkAuthorityContract.maximumPrefixes, networkPrefix), value => value);
|
|
174
159
|
requireValue(prefixes.length > 0);
|
|
175
160
|
nonoverlapping(prefixes);
|
|
176
161
|
const protectedPrefix = (value: string) => requireValue(prefixes.some(range => contains(range, value)));
|
|
177
|
-
const pools = sorted(list(item.pools,
|
|
162
|
+
const pools = sorted(list(item.pools, runtimeNetworkAuthorityContract.maximumPools, value => {
|
|
178
163
|
const pool = record(value, ['id', 'purpose', 'prefix']);
|
|
179
164
|
requireValue(pool.purpose === 'workload' || pool.purpose === 'transit');
|
|
180
|
-
const result:
|
|
181
|
-
id: id(pool.id), purpose: pool.purpose, prefix:
|
|
165
|
+
const result: IRuntimeNetworkProtectedAuthority['pools'][number] = {
|
|
166
|
+
id: id(pool.id), purpose: pool.purpose, prefix: networkPrivatePrefix(pool.prefix),
|
|
182
167
|
};
|
|
183
168
|
protectedPrefix(result.prefix);
|
|
184
169
|
return result;
|
|
@@ -191,35 +176,34 @@ export const snapshotRuntimeNetworkProtectedAuthorityV1 = (value: unknown): IRun
|
|
|
191
176
|
requireValue(pools.every(pool => !contains(pool.prefix, `${result.address}/32`)));
|
|
192
177
|
return withProtocol ? { ...result, protocol: protocol(endpoint.protocol) } : result;
|
|
193
178
|
};
|
|
194
|
-
const resolvers = sorted(list(item.resolvers,
|
|
179
|
+
const resolvers = sorted(list(item.resolvers, runtimeNetworkAuthorityContract.maximumResolvers,
|
|
195
180
|
value => readEndpoint(value, false)), value => value.id);
|
|
196
|
-
const platformEndpoints = sorted(list(item.platformEndpoints,
|
|
197
|
-
value => readEndpoint(value, true) as
|
|
181
|
+
const platformEndpoints = sorted(list(item.platformEndpoints, runtimeNetworkAuthorityContract.maximumPlatformEndpoints,
|
|
182
|
+
value => readEndpoint(value, true) as IRuntimeNetworkProtectedAuthority['platformEndpoints'][number]), value => value.id);
|
|
198
183
|
const allEndpointKeys = [...resolvers.flatMap(value => ['tcp', 'udp'].map(transport => `${value.address}:${value.port}:${transport}`)),
|
|
199
184
|
...platformEndpoints.map(value => `${value.address}:${value.port}:${value.protocol}`)];
|
|
200
185
|
requireValue(new Set(allEndpointKeys).size === allEndpointKeys.length);
|
|
201
|
-
const egressAuthorities = sorted(list(item.egressAuthorities,
|
|
186
|
+
const egressAuthorities = sorted(list(item.egressAuthorities, runtimeNetworkAuthorityContract.maximumEgressAuthorities, value => {
|
|
202
187
|
const authority = record(value, ['nodeId', 'runtimeNamespace']);
|
|
203
188
|
return { nodeId: id(authority.nodeId), runtimeNamespace: id(authority.runtimeNamespace) };
|
|
204
189
|
}), value => canonical([value.nodeId, value.runtimeNamespace]));
|
|
205
190
|
requireValue(egressAuthorities.length > 0);
|
|
206
|
-
return {
|
|
191
|
+
return { id: identity, generation, controller: controller(item.controller), previous,
|
|
207
192
|
prefixes, pools, resolvers, platformEndpoints, egressAuthorities, digest: digest(item.digest) };
|
|
208
193
|
});
|
|
209
194
|
|
|
210
|
-
export const
|
|
211
|
-
const item = record(value, ['
|
|
195
|
+
export const snapshotRuntimeNetworkHandoffLease = (value: unknown): IRuntimeNetworkHandoffLease => safe(() => {
|
|
196
|
+
const item = record(value, ['id', 'generation', 'controller', 'nodeId', 'runtimeNamespace',
|
|
212
197
|
'protectedAuthority', 'routerIncarnationId', 'transit', 'sourcePortRanges', 'conntrackZone', 'conntrackLabel', 'digest']);
|
|
213
|
-
requireValue(item.schemaVersion === 1);
|
|
214
198
|
const transit = record(item.transit, ['poolId', 'prefix', 'routerAddress', 'hostAddress']);
|
|
215
|
-
const transitPrefix =
|
|
199
|
+
const transitPrefix = networkPrivatePrefix(transit.prefix), [first, last] = bounds(transitPrefix);
|
|
216
200
|
const routerAddress = endpointAddress(transit.routerAddress), hostAddress = endpointAddress(transit.hostAddress);
|
|
217
201
|
requireValue(first !== last && routerAddress !== hostAddress);
|
|
218
202
|
for (const value of [routerAddress, hostAddress]) {
|
|
219
203
|
const number = numericAddress(value);
|
|
220
204
|
requireValue(number >= first && number <= last && (last - first === 1 || (number !== first && number !== last)));
|
|
221
205
|
}
|
|
222
|
-
const sourcePortRanges = list(item.sourcePortRanges,
|
|
206
|
+
const sourcePortRanges = list(item.sourcePortRanges, runtimeNetworkAuthorityContract.maximumPortRanges, value => {
|
|
223
207
|
const range = record(value, ['protocol', 'first', 'last']);
|
|
224
208
|
const result = { protocol: protocol(range.protocol), first: integer(range.first, 65535), last: integer(range.last, 65535) };
|
|
225
209
|
requireValue(result.first <= result.last);
|
|
@@ -232,7 +216,7 @@ export const snapshotRuntimeNetworkHandoffLeaseV1 = (value: unknown): IRuntimeNe
|
|
|
232
216
|
}
|
|
233
217
|
requireValue(typeof item.conntrackLabel === 'string' && /^[a-f0-9]{32}$/.test(item.conntrackLabel)
|
|
234
218
|
&& item.conntrackLabel !== '0'.repeat(32));
|
|
235
|
-
return {
|
|
219
|
+
return { id: id(item.id), generation: integer(item.generation), controller: controller(item.controller),
|
|
236
220
|
nodeId: id(item.nodeId), runtimeNamespace: id(item.runtimeNamespace), protectedAuthority: reference(item.protectedAuthority),
|
|
237
221
|
routerIncarnationId: id(item.routerIncarnationId), transit: { poolId: id(transit.poolId), prefix: transitPrefix, routerAddress, hostAddress },
|
|
238
222
|
sourcePortRanges, conntrackZone: integer(item.conntrackZone, 65535), conntrackLabel: item.conntrackLabel, digest: digest(item.digest) };
|
|
@@ -242,19 +226,19 @@ const computeDigest = async (domain: string, value: { digest: TSha256Digest }):
|
|
|
242
226
|
const { digest: omitted, ...payload } = value;
|
|
243
227
|
return `sha256:${await createCanonicalJsonSha256Hex(canonical({ domain, payload }), fail)}`;
|
|
244
228
|
};
|
|
245
|
-
export const
|
|
246
|
-
computeDigest(
|
|
247
|
-
export const
|
|
248
|
-
computeDigest(
|
|
229
|
+
export const computeRuntimeNetworkProtectedAuthorityDigest = (value: IRuntimeNetworkProtectedAuthority): Promise<TSha256Digest> =>
|
|
230
|
+
computeDigest(runtimeNetworkAuthorityContract.protectedDigestDomain, snapshotRuntimeNetworkProtectedAuthority(value));
|
|
231
|
+
export const computeRuntimeNetworkHandoffLeaseDigest = (value: IRuntimeNetworkHandoffLease): Promise<TSha256Digest> =>
|
|
232
|
+
computeDigest(runtimeNetworkAuthorityContract.handoffDigestDomain, snapshotRuntimeNetworkHandoffLease(value));
|
|
249
233
|
|
|
250
234
|
/** Detached, digest-verified allocation binding. Does not establish pool activation or live link identity. */
|
|
251
|
-
export const
|
|
252
|
-
lease:
|
|
235
|
+
export const bindRuntimeNetworkHandoffLease = async (leaseValue: unknown, authorityValue: unknown): Promise<{
|
|
236
|
+
lease: IRuntimeNetworkHandoffLease; authority: IRuntimeNetworkProtectedAuthority;
|
|
253
237
|
}> => {
|
|
254
|
-
const lease =
|
|
255
|
-
const authority =
|
|
256
|
-
requireValue(lease.digest === await
|
|
257
|
-
requireValue(authority.digest === await
|
|
238
|
+
const lease = snapshotRuntimeNetworkHandoffLease(leaseValue);
|
|
239
|
+
const authority = snapshotRuntimeNetworkProtectedAuthority(authorityValue);
|
|
240
|
+
requireValue(lease.digest === await computeRuntimeNetworkHandoffLeaseDigest(lease));
|
|
241
|
+
requireValue(authority.digest === await computeRuntimeNetworkProtectedAuthorityDigest(authority));
|
|
258
242
|
requireValue(equals(lease.controller, authority.controller)
|
|
259
243
|
&& equals(lease.protectedAuthority, { id: authority.id, generation: authority.generation, digest: authority.digest })
|
|
260
244
|
&& authority.egressAuthorities.some(value => value.nodeId === lease.nodeId && value.runtimeNamespace === lease.runtimeNamespace));
|
|
@@ -268,12 +252,12 @@ export const bindRuntimeNetworkHandoffLeaseV1 = async (leaseValue: unknown, auth
|
|
|
268
252
|
* The owning transactional ledger must supply its complete history and serialize reservations.
|
|
269
253
|
* Namespace replacement, an empty CT dump or process exit never removes an entry from this set.
|
|
270
254
|
*/
|
|
271
|
-
export const
|
|
272
|
-
const leases = safe(() => list(value,
|
|
273
|
-
|
|
255
|
+
export const snapshotRuntimeNetworkHandoffLedger = async (value: unknown): Promise<IRuntimeNetworkHandoffLease[]> => {
|
|
256
|
+
const leases = safe(() => list(value, runtimeNetworkAuthorityContract.maximumRetainedHandoffLeases,
|
|
257
|
+
snapshotRuntimeNetworkHandoffLease));
|
|
274
258
|
for (let index = 0; index < leases.length; index++) {
|
|
275
259
|
const lease = leases[index];
|
|
276
|
-
requireValue(lease.digest === await
|
|
260
|
+
requireValue(lease.digest === await computeRuntimeNetworkHandoffLeaseDigest(lease));
|
|
277
261
|
for (let other = 0; other < index; other++) {
|
|
278
262
|
const previous = leases[other];
|
|
279
263
|
requireValue(lease.nodeId === previous.nodeId && equals(lease.controller, previous.controller));
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { IRuntimeAuthorityReference } from './runtimeassignment.js';
|
|
2
|
+
import { snapshotRuntimeSessionBinding, type IRuntimeSessionBinding } from './runtimesession.js';
|
|
3
|
+
import { validateRuntimeNetworkProjection, type IRuntimeNetworkProjection } from './networkprojection.js';
|
|
4
4
|
import { snapshotRuntimeNetworkDnsLeaseRenewal, validateRuntimeNetworkDnsLeaseRenewal } from './networklease.js';
|
|
5
5
|
import { captureNetwork, failNetwork, networkCounter, networkEffectiveDnsWindow, networkEqual, networkId, networkIpv4,
|
|
6
6
|
networkRecord, networkReference, networkReferenceOf, networkSafe, networkSameScope, requireNetwork } from '../private/networkcontract.js';
|
|
7
7
|
|
|
8
|
-
export interface
|
|
9
|
-
session:
|
|
10
|
-
projection:
|
|
8
|
+
export interface IGetRuntimeManagedVpnCredentialRequest {
|
|
9
|
+
session: IRuntimeSessionBinding;
|
|
10
|
+
projection: IRuntimeAuthorityReference;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/** Secret response over the current authenticated physical peer only. Never
|
|
14
14
|
* persist, hash, log, or include it in a signed network projection. The native
|
|
15
15
|
* Noise owner validates and uses the keys; these shapes confer no TUN readiness. */
|
|
16
|
-
export interface
|
|
16
|
+
export interface IGetRuntimeManagedVpnCredentialResponse extends IGetRuntimeManagedVpnCredentialRequest {
|
|
17
17
|
transport: 'quic';
|
|
18
18
|
relay: { id: string; address: string; port: number };
|
|
19
19
|
authorityId: string;
|
|
@@ -36,35 +36,35 @@ const key = (value: unknown): string => {
|
|
|
36
36
|
return value;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
export const
|
|
39
|
+
export const snapshotGetRuntimeManagedVpnCredentialRequest = (value: unknown): IGetRuntimeManagedVpnCredentialRequest => networkSafe(() => {
|
|
40
40
|
const item = networkRecord(captureNetwork(value), ['session', 'projection']);
|
|
41
|
-
return { session:
|
|
41
|
+
return { session: snapshotRuntimeSessionBinding(item.session), projection: networkReference(item.projection) };
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
export const
|
|
44
|
+
export const snapshotGetRuntimeManagedVpnCredentialResponse = (value: unknown): IGetRuntimeManagedVpnCredentialResponse => networkSafe(() => {
|
|
45
45
|
const item = networkRecord(captureNetwork(value), ['session', 'projection', 'transport', 'relay', 'authorityId',
|
|
46
46
|
'serverPublicKey', 'clientPublicKey', 'clientPrivateKey', 'expiresAt']);
|
|
47
47
|
const relay = networkRecord(item.relay, ['id', 'address', 'port']); requireNetwork(item.transport === 'quic');
|
|
48
|
-
return { session:
|
|
48
|
+
return { session: snapshotRuntimeSessionBinding(item.session), projection: networkReference(item.projection), transport: 'quic',
|
|
49
49
|
relay: { id: networkId(relay.id), address: networkIpv4(relay.address), port: networkCounter(relay.port, 65535) },
|
|
50
50
|
authorityId: networkId(item.authorityId), serverPublicKey: key(item.serverPublicKey), clientPublicKey: key(item.clientPublicKey),
|
|
51
51
|
clientPrivateKey: key(item.clientPrivateKey), expiresAt: networkCounter(item.expiresAt) };
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
const bindRequest = (request:
|
|
55
|
-
projection:
|
|
54
|
+
const bindRequest = (request: IGetRuntimeManagedVpnCredentialRequest,
|
|
55
|
+
projection: IRuntimeNetworkProjection, trusted: IRuntimeSessionBinding): void => {
|
|
56
56
|
requireNetwork(networkEqual(request.session, trusted) && networkEqual(request.projection, networkReferenceOf(projection))
|
|
57
57
|
&& networkSameScope(projection, trusted) && projection.handoff);
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
/** Content binding only. The caller must resolve the exact current projection
|
|
61
61
|
* and authenticate/fence the current physical peer before and after native work. */
|
|
62
|
-
export const
|
|
63
|
-
trustedSessionValue: unknown): Promise<
|
|
62
|
+
export const bindGetRuntimeManagedVpnCredentialRequest = async (value: unknown, projectionValue: unknown,
|
|
63
|
+
trustedSessionValue: unknown): Promise<IGetRuntimeManagedVpnCredentialRequest> => {
|
|
64
64
|
try {
|
|
65
|
-
const request =
|
|
66
|
-
const trusted =
|
|
67
|
-
const projection = await
|
|
65
|
+
const request = snapshotGetRuntimeManagedVpnCredentialRequest(value);
|
|
66
|
+
const trusted = snapshotRuntimeSessionBinding(trustedSessionValue);
|
|
67
|
+
const projection = await validateRuntimeNetworkProjection(projectionValue);
|
|
68
68
|
bindRequest(request, projection, trusted);
|
|
69
69
|
return request;
|
|
70
70
|
} catch { return failNetwork(); }
|
|
@@ -76,15 +76,15 @@ export const bindGetRuntimeManagedVpnCredentialRequestV1 = async (value: unknown
|
|
|
76
76
|
* projection, or null; this helper verifies no signature. Time is caller-qualified;
|
|
77
77
|
* this helper does not sample a clock, authenticate a transport, admit policy or
|
|
78
78
|
* prove a running managed connection. */
|
|
79
|
-
export const
|
|
79
|
+
export const bindGetRuntimeManagedVpnCredentialResponse = async (value: unknown, requestValue: unknown,
|
|
80
80
|
projectionValue: unknown, trustedSessionValue: unknown, nowValue: unknown,
|
|
81
|
-
renewalValue: unknown | null = null): Promise<
|
|
81
|
+
renewalValue: unknown | null = null): Promise<IGetRuntimeManagedVpnCredentialResponse> => {
|
|
82
82
|
try {
|
|
83
|
-
const response =
|
|
84
|
-
const request =
|
|
85
|
-
const trusted =
|
|
83
|
+
const response = snapshotGetRuntimeManagedVpnCredentialResponse(value);
|
|
84
|
+
const request = snapshotGetRuntimeManagedVpnCredentialRequest(requestValue);
|
|
85
|
+
const trusted = snapshotRuntimeSessionBinding(trustedSessionValue), now = networkCounter(nowValue);
|
|
86
86
|
const renewalInput = renewalValue === null ? null : snapshotRuntimeNetworkDnsLeaseRenewal(renewalValue);
|
|
87
|
-
const projection = await
|
|
87
|
+
const projection = await validateRuntimeNetworkProjection(projectionValue);
|
|
88
88
|
const renewal = renewalInput === null ? null : await validateRuntimeNetworkDnsLeaseRenewal(renewalInput);
|
|
89
89
|
const window = networkEffectiveDnsWindow(projection, renewal);
|
|
90
90
|
bindRequest(request, projection, trusted);
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
1
|
+
import type { IRuntimeAuthorityReference } from './runtimeassignment.js';
|
|
2
|
+
import type { IRuntimeSessionBinding } from './runtimesession.js';
|
|
3
|
+
import { snapshotRuntimeSessionBinding } from './runtimesession.js';
|
|
4
|
+
import type { IRuntimeNetworkSigningAuthority, ISignedRuntimeNetworkProjection } from './networksigning.js';
|
|
5
|
+
import { snapshotRuntimeNetworkSigningAuthority, snapshotSignedRuntimeNetworkProjection } from './networksigning.js';
|
|
6
6
|
import type { IRuntimeNetworkDnsLeaseRenewalReference, ISignedRuntimeNetworkDnsLeaseRenewal } from './networklease.js';
|
|
7
7
|
import { getRuntimeNetworkDnsLeaseRenewalReference, snapshotRuntimeNetworkDnsLeaseRenewal,
|
|
8
8
|
snapshotRuntimeNetworkDnsLeaseRenewalReference, snapshotSignedRuntimeNetworkDnsLeaseRenewal } from './networklease.js';
|
|
9
9
|
import { captureNetwork, networkEqual, networkRecord, networkReference, networkReferenceOf, networkSafe, networkSameScope,
|
|
10
10
|
requireNetwork } from '../private/networkcontract.js';
|
|
11
11
|
|
|
12
|
-
export interface
|
|
13
|
-
session:
|
|
14
|
-
authority:
|
|
12
|
+
export interface IApplyRuntimeNetworkSigningAuthorityRequest {
|
|
13
|
+
session: IRuntimeSessionBinding;
|
|
14
|
+
authority: IRuntimeNetworkSigningAuthority;
|
|
15
15
|
}
|
|
16
|
-
export interface
|
|
17
|
-
session:
|
|
18
|
-
signed:
|
|
16
|
+
export interface IApplyRuntimeNetworkProjectionRequest {
|
|
17
|
+
session: IRuntimeSessionBinding;
|
|
18
|
+
signed: ISignedRuntimeNetworkProjection;
|
|
19
19
|
}
|
|
20
20
|
/** Durable admission only. No packet, DNS, pool-activation, fencing or readiness assertion. */
|
|
21
|
-
export interface
|
|
21
|
+
export interface IApplyRuntimeNetworkAuthorityResponse {
|
|
22
22
|
status: 'accepted' | 'replay';
|
|
23
|
-
authority:
|
|
23
|
+
authority: IRuntimeAuthorityReference;
|
|
24
24
|
}
|
|
25
25
|
export interface IApplyRuntimeNetworkDnsLeaseRenewalRequest {
|
|
26
|
-
session:
|
|
26
|
+
session: IRuntimeSessionBinding;
|
|
27
27
|
signed: ISignedRuntimeNetworkDnsLeaseRenewal;
|
|
28
28
|
}
|
|
29
29
|
/** Durable admission only. It does not assert that the node converted the window or serves DNS. */
|
|
@@ -36,22 +36,22 @@ const admissionStatus = (value: unknown): 'accepted' | 'replay' => {
|
|
|
36
36
|
requireNetwork(value === 'accepted' || value === 'replay'); return value;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
export const
|
|
39
|
+
export const snapshotApplyRuntimeNetworkSigningAuthorityRequest = (value: unknown): IApplyRuntimeNetworkSigningAuthorityRequest => networkSafe(() => {
|
|
40
40
|
const item = networkRecord(captureNetwork(value), ['session', 'authority']);
|
|
41
|
-
return { session:
|
|
41
|
+
return { session: snapshotRuntimeSessionBinding(item.session), authority: snapshotRuntimeNetworkSigningAuthority(item.authority) };
|
|
42
42
|
});
|
|
43
|
-
export const
|
|
43
|
+
export const snapshotApplyRuntimeNetworkProjectionRequest = (value: unknown): IApplyRuntimeNetworkProjectionRequest => networkSafe(() => {
|
|
44
44
|
const item = networkRecord(captureNetwork(value), ['session', 'signed']);
|
|
45
|
-
return { session:
|
|
45
|
+
return { session: snapshotRuntimeSessionBinding(item.session), signed: snapshotSignedRuntimeNetworkProjection(item.signed) };
|
|
46
46
|
});
|
|
47
|
-
export const
|
|
47
|
+
export const snapshotApplyRuntimeNetworkAuthorityResponse = (value: unknown): IApplyRuntimeNetworkAuthorityResponse => networkSafe(() => {
|
|
48
48
|
const item = networkRecord(captureNetwork(value), ['status', 'authority']);
|
|
49
49
|
return { status: admissionStatus(item.status), authority: networkReference(item.authority) };
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
export const snapshotApplyRuntimeNetworkDnsLeaseRenewalRequest = (value: unknown): IApplyRuntimeNetworkDnsLeaseRenewalRequest => networkSafe(() => {
|
|
53
53
|
const item = networkRecord(captureNetwork(value), ['session', 'signed']);
|
|
54
|
-
return { session:
|
|
54
|
+
return { session: snapshotRuntimeSessionBinding(item.session), signed: snapshotSignedRuntimeNetworkDnsLeaseRenewal(item.signed) };
|
|
55
55
|
});
|
|
56
56
|
export const snapshotApplyRuntimeNetworkDnsLeaseRenewalResponse = (value: unknown): IApplyRuntimeNetworkDnsLeaseRenewalResponse => networkSafe(() => {
|
|
57
57
|
const item = networkRecord(captureNetwork(value), ['status', 'renewal']);
|
|
@@ -59,22 +59,22 @@ export const snapshotApplyRuntimeNetworkDnsLeaseRenewalResponse = (value: unknow
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
/** trustedSessionValue must come from the receiving CURRENT physical connection. */
|
|
62
|
-
export const
|
|
63
|
-
const request =
|
|
62
|
+
export const bindApplyRuntimeNetworkSigningAuthorityRequest = (value: unknown, trustedSessionValue: unknown): IApplyRuntimeNetworkSigningAuthorityRequest => networkSafe(() => {
|
|
63
|
+
const request = snapshotApplyRuntimeNetworkSigningAuthorityRequest(value), session = snapshotRuntimeSessionBinding(trustedSessionValue);
|
|
64
64
|
requireNetwork(networkEqual(request.session, session) && networkEqual(request.authority.controller, session.controller));
|
|
65
65
|
return request;
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
/** Content binding only. Verify signature with stored trust and atomically fence admission. */
|
|
69
|
-
export const
|
|
70
|
-
const request =
|
|
69
|
+
export const bindApplyRuntimeNetworkProjectionRequest = (value: unknown, trustedSessionValue: unknown): IApplyRuntimeNetworkProjectionRequest => networkSafe(() => {
|
|
70
|
+
const request = snapshotApplyRuntimeNetworkProjectionRequest(value), session = snapshotRuntimeSessionBinding(trustedSessionValue);
|
|
71
71
|
requireNetwork(networkEqual(request.session, session) && networkSameScope(request.signed.projection, session));
|
|
72
72
|
return request;
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
/** Compare ACK with the exact SENT key or projection reference, never with a response-derived value. */
|
|
76
|
-
export const
|
|
77
|
-
const response =
|
|
76
|
+
export const bindApplyRuntimeNetworkAuthorityResponse = (value: unknown, sentReferenceValue: unknown): IApplyRuntimeNetworkAuthorityResponse => networkSafe(() => {
|
|
77
|
+
const response = snapshotApplyRuntimeNetworkAuthorityResponse(value);
|
|
78
78
|
const expected = networkReference(captureNetwork(sentReferenceValue));
|
|
79
79
|
requireNetwork(networkEqual(response.authority, networkReferenceOf(expected)));
|
|
80
80
|
return response;
|
|
@@ -87,7 +87,7 @@ export const bindApplyRuntimeNetworkAuthorityResponseV1 = (value: unknown, sentR
|
|
|
87
87
|
* stored trust, then admit and fence atomically.
|
|
88
88
|
*/
|
|
89
89
|
export const bindApplyRuntimeNetworkDnsLeaseRenewalRequest = (value: unknown, trustedSessionValue: unknown): IApplyRuntimeNetworkDnsLeaseRenewalRequest => networkSafe(() => {
|
|
90
|
-
const request = snapshotApplyRuntimeNetworkDnsLeaseRenewalRequest(value), session =
|
|
90
|
+
const request = snapshotApplyRuntimeNetworkDnsLeaseRenewalRequest(value), session = snapshotRuntimeSessionBinding(trustedSessionValue);
|
|
91
91
|
requireNetwork(networkEqual(request.session, session) && networkSameScope(request.signed.renewal, session));
|
|
92
92
|
return request;
|
|
93
93
|
});
|