@serve.zone/interfaces 31.1.0 → 31.3.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 +46 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/cluster.d.ts +85 -0
- package/dist_ts/data/cluster.js +137 -1
- package/dist_ts/data/privatenetwork.js +3 -5
- package/dist_ts/private/ipv4.d.ts +9 -0
- package/dist_ts/private/ipv4.js +23 -0
- package/dist_ts/requests/cluster.d.ts +47 -0
- package/package.json +1 -1
- package/readme.md +76 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/cluster.ts +230 -0
- package/ts/data/privatenetwork.ts +3 -4
- package/ts/private/ipv4.ts +24 -0
- package/ts/requests/cluster.ts +59 -0
package/changelog.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-09-16 - 31.3.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Add the cluster relay enable, disable and registration contracts. `enableClusterRelayV1` and
|
|
8
|
+
`disableClusterRelayV1` take only an identity and a cluster id and answer with the published
|
|
9
|
+
`IClusterRelayV1` block or an explicit `relay: null`: Cloudly derives the origin from the
|
|
10
|
+
cluster and its own public hostname and refuses when no DNS zone it manages matches the derived
|
|
11
|
+
name, so no caller can name a relay origin. `registerClusterRelayV1` is what the relay sends
|
|
12
|
+
over its own socket after `registerCloudlyClientSessionV1`, carrying
|
|
13
|
+
`IClusterRelayRegistrationV1 { schemaVersion: 1; relay: { version; nodeId; listenAddress;
|
|
14
|
+
listenPort }; registrationSequence }` and answered with
|
|
15
|
+
`IClusterRelayRegistrationResponseV1 { accepted: true; origin: string | null }` — the origin
|
|
16
|
+
Cloudly published, so the relay can check the certificate it holds against the name its nodes
|
|
17
|
+
are given, and `null` while the cluster carries no relay block.
|
|
18
|
+
`validateClusterRelayRegistrationV1(value, pathArg?)` returns one named reason per violation in
|
|
19
|
+
the package's value-free validator shape, and checks shape only: `listenAddress` must be a
|
|
20
|
+
unicast IPv4 literal, because Cloudly publishes it verbatim as an A record, and a loopback or
|
|
21
|
+
link-local address is refused by its own name because a relay that bound one can never serve
|
|
22
|
+
its cluster; `nodeId` follows the same rule a node's own runtime session registers under;
|
|
23
|
+
`registrationSequence` is a non-negative safe integer, so a relay process's first registration
|
|
24
|
+
at `0` is a registration. Which cluster a relay speaks for, whether its node belongs to that
|
|
25
|
+
cluster, and which of two live peers is the relay all stay Cloudly's decisions, made from the
|
|
26
|
+
verified identity on the socket and its own records. Additive only: the dotted-quad rule the
|
|
27
|
+
private network upstream validator already applied moved into one shared internal predicate
|
|
28
|
+
with unchanged semantics, and nothing was removed or renamed.
|
|
29
|
+
|
|
30
|
+
## 2026-09-16 - 31.2.0
|
|
31
|
+
|
|
32
|
+
### Features
|
|
33
|
+
|
|
34
|
+
- Add the optional `ICluster.data.relay` block, `IClusterRelayV1 { schemaVersion: 1; origin: string }`:
|
|
35
|
+
how cluster-side components reach this cluster's relay, absent on a cluster whose nodes still
|
|
36
|
+
target Cloudly directly, so its presence is what marks a cluster as relayed. `origin` is the
|
|
37
|
+
exact value a Pallet node stores as its `relayOrigin` — `https:`, an explicit port allowed, no
|
|
38
|
+
path, query, fragment, userinfo or trailing slash, so `new URL(origin).origin === origin`; a
|
|
39
|
+
default `:443` and a cased host are therefore not canonical, because `URL` drops the one and
|
|
40
|
+
lowercases the other. `validateClusterRelayV1(value, pathArg?)` returns one named reason per
|
|
41
|
+
violation and an empty array for a valid block, following the package's value-free validator
|
|
42
|
+
shape. The certificate subject is deliberately not a second field: every consumer derives it
|
|
43
|
+
from the origin with `clusterRelayCertificateDomainName(relay)`, which returns the origin's
|
|
44
|
+
hostname — the name Cloudly issues the certificate for and owns the A record for — so two
|
|
45
|
+
fields can never disagree. An origin naming an address literal consequently has no issuable
|
|
46
|
+
certificate. Additive only: a cluster document without `relay` is unchanged, and nothing was
|
|
47
|
+
removed or renamed.
|
|
48
|
+
|
|
3
49
|
## 2026-09-16 - 31.1.0
|
|
4
50
|
|
|
5
51
|
### Features
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/interfaces',
|
|
6
|
-
version: '31.
|
|
6
|
+
version: '31.3.0',
|
|
7
7
|
description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
|
|
@@ -28,5 +28,90 @@ export interface ICluster {
|
|
|
28
28
|
serverSecret: string;
|
|
29
29
|
};
|
|
30
30
|
sshKeys: plugins.tsclass.network.ISshKey[];
|
|
31
|
+
/**
|
|
32
|
+
* How cluster-side components reach this cluster's relay. Absent on a
|
|
33
|
+
* cluster whose nodes still target Cloudly directly.
|
|
34
|
+
*/
|
|
35
|
+
relay?: IClusterRelayV1;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* How cluster-side components reach this cluster's relay.
|
|
40
|
+
*
|
|
41
|
+
* `origin` is the exact value a Pallet node stores as its `relayOrigin`: an
|
|
42
|
+
* `https:` origin, an explicit port allowed, with no path, no query, no
|
|
43
|
+
* fragment, no userinfo and no trailing slash, so that
|
|
44
|
+
* `new URL(origin).origin === origin`. A default `:443` is therefore written
|
|
45
|
+
* implicitly, because `URL` drops it from the origin it computes.
|
|
46
|
+
*/
|
|
47
|
+
export interface IClusterRelayV1 {
|
|
48
|
+
schemaVersion: 1;
|
|
49
|
+
origin: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* What a cluster relay tells Cloudly about itself once its socket is authenticated.
|
|
53
|
+
*
|
|
54
|
+
* The relay sends this after `registerCloudlyClientSessionV1`, so the cluster it registers for
|
|
55
|
+
* is the one its verified machine identity names and is never a field here. `relay.nodeId` is
|
|
56
|
+
* the cluster node the relay process runs on, `listenAddress` and `listenPort` are the exact
|
|
57
|
+
* endpoint it bound for the Pallet nodes of its cluster, and `registrationSequence` counts up
|
|
58
|
+
* once per registration within one relay process, so Cloudly can tell a newer registration from
|
|
59
|
+
* a replayed older one while both peers are live.
|
|
60
|
+
*/
|
|
61
|
+
export interface IClusterRelayRegistrationV1 {
|
|
62
|
+
schemaVersion: 1;
|
|
63
|
+
relay: {
|
|
64
|
+
/** The relay build that is registering; a bounded label for operators and logs. */
|
|
65
|
+
version: string;
|
|
66
|
+
nodeId: string;
|
|
67
|
+
/**
|
|
68
|
+
* The address the relay bound, as an IPv4 literal.
|
|
69
|
+
*
|
|
70
|
+
* Cloudly publishes this value verbatim as the A record of the relay's certificate domain
|
|
71
|
+
* name, so the contract admits exactly what that record can carry: a hostname would need a
|
|
72
|
+
* `CNAME` and an IPv6 address an `AAAA` record, and neither is published today. Admitting
|
|
73
|
+
* either is an additive change once Cloudly publishes it.
|
|
74
|
+
*/
|
|
75
|
+
listenAddress: string;
|
|
76
|
+
listenPort: number;
|
|
31
77
|
};
|
|
78
|
+
/** Monotonic within one relay process; it restarts at 0 when the process restarts. */
|
|
79
|
+
registrationSequence: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* What Cloudly tells the relay back.
|
|
83
|
+
*
|
|
84
|
+
* `origin` is the origin Cloudly has published for this cluster, so a relay can check that the
|
|
85
|
+
* certificate it holds and the name its nodes are told to reach are the same one. It is `null`
|
|
86
|
+
* while the cluster carries no relay block, which a relay must treat as "not enabled yet"
|
|
87
|
+
* rather than as an origin it may invent.
|
|
88
|
+
*/
|
|
89
|
+
export interface IClusterRelayRegistrationResponseV1 {
|
|
90
|
+
accepted: true;
|
|
91
|
+
origin: string | null;
|
|
32
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Validates one cluster relay block and names every reason it is refused.
|
|
95
|
+
*
|
|
96
|
+
* A relay whose origin cannot be trusted verbatim must never reach a node's
|
|
97
|
+
* identity store: it is the transport authority the node's runtime session
|
|
98
|
+
* registers over.
|
|
99
|
+
*/
|
|
100
|
+
export declare const validateClusterRelayV1: (valueArg: unknown, pathArg?: string) => string[];
|
|
101
|
+
/**
|
|
102
|
+
* Validates one cluster relay registration and names every reason it is refused.
|
|
103
|
+
*
|
|
104
|
+
* Shape only, exactly like every other validator here: which cluster the relay speaks for comes
|
|
105
|
+
* from the verified identity on its socket, whether `relay.nodeId` is a node of that cluster is
|
|
106
|
+
* Cloudly's to decide against its own records, and whether this registration supersedes a live
|
|
107
|
+
* one is Cloudly's comparison of `registrationSequence` between peers. A registration that
|
|
108
|
+
* passes here has claimed nothing.
|
|
109
|
+
*/
|
|
110
|
+
export declare const validateClusterRelayRegistrationV1: (valueArg: unknown, pathArg?: string) => string[];
|
|
111
|
+
/**
|
|
112
|
+
* The name the relay's certificate is issued for, and the A record Cloudly
|
|
113
|
+
* owns for it. Derived from the origin so every consumer derives it
|
|
114
|
+
* identically; an origin that names an address literal instead of a domain
|
|
115
|
+
* therefore has no issuable certificate.
|
|
116
|
+
*/
|
|
117
|
+
export declare const clusterRelayCertificateDomainName: (relayArg: IClusterRelayV1) => string;
|
package/dist_ts/data/cluster.js
CHANGED
|
@@ -1,3 +1,139 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
import {} from '../data/docker.js';
|
|
3
|
-
|
|
3
|
+
import { isPlausibleUnicastIpv4 } from '../private/ipv4.js';
|
|
4
|
+
const maximumClusterRelayOriginLength = 2048;
|
|
5
|
+
const maximumClusterRelayVersionLength = 64;
|
|
6
|
+
const isRecord = (valueArg) => (Boolean(valueArg)
|
|
7
|
+
&& typeof valueArg === 'object'
|
|
8
|
+
&& !Array.isArray(valueArg)
|
|
9
|
+
&& (Object.getPrototypeOf(valueArg) === Object.prototype
|
|
10
|
+
|| Object.getPrototypeOf(valueArg) === null));
|
|
11
|
+
const hasExactKeys = (valueArg, keysArg) => {
|
|
12
|
+
const actualKeys = Object.keys(valueArg).sort();
|
|
13
|
+
const expectedKeys = [...keysArg].sort();
|
|
14
|
+
return actualKeys.length === expectedKeys.length
|
|
15
|
+
&& actualKeys.every((keyArg, indexArg) => keyArg === expectedKeys[indexArg]);
|
|
16
|
+
};
|
|
17
|
+
const validateClusterRelayOrigin = (valueArg, pathArg) => {
|
|
18
|
+
if (typeof valueArg !== 'string'
|
|
19
|
+
|| valueArg.length === 0
|
|
20
|
+
|| valueArg.length > maximumClusterRelayOriginLength) {
|
|
21
|
+
return [`${pathArg} must be a string of 1 to ${maximumClusterRelayOriginLength} characters`];
|
|
22
|
+
}
|
|
23
|
+
let parsedOrigin;
|
|
24
|
+
try {
|
|
25
|
+
parsedOrigin = new URL(valueArg);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return [`${pathArg} must be a parsable URL`];
|
|
29
|
+
}
|
|
30
|
+
const errors = [];
|
|
31
|
+
if (parsedOrigin.protocol !== 'https:') {
|
|
32
|
+
errors.push(`${pathArg} must use the https scheme`);
|
|
33
|
+
}
|
|
34
|
+
if (parsedOrigin.username || parsedOrigin.password) {
|
|
35
|
+
errors.push(`${pathArg} must not carry userinfo`);
|
|
36
|
+
}
|
|
37
|
+
if (parsedOrigin.origin !== valueArg) {
|
|
38
|
+
errors.push(`${pathArg} must be the canonical origin ${parsedOrigin.origin}, `
|
|
39
|
+
+ 'without a path, query, fragment, trailing slash or default port');
|
|
40
|
+
}
|
|
41
|
+
return errors;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Validates one cluster relay block and names every reason it is refused.
|
|
45
|
+
*
|
|
46
|
+
* A relay whose origin cannot be trusted verbatim must never reach a node's
|
|
47
|
+
* identity store: it is the transport authority the node's runtime session
|
|
48
|
+
* registers over.
|
|
49
|
+
*/
|
|
50
|
+
export const validateClusterRelayV1 = (valueArg, pathArg = 'cluster relay') => {
|
|
51
|
+
if (!isRecord(valueArg) || !hasExactKeys(valueArg, ['schemaVersion', 'origin'])) {
|
|
52
|
+
return [`${pathArg} must use its exact schema`];
|
|
53
|
+
}
|
|
54
|
+
const errors = [];
|
|
55
|
+
if (valueArg.schemaVersion !== 1) {
|
|
56
|
+
errors.push(`${pathArg} schemaVersion must be 1`);
|
|
57
|
+
}
|
|
58
|
+
errors.push(...validateClusterRelayOrigin(valueArg.origin, `${pathArg} origin`));
|
|
59
|
+
return errors;
|
|
60
|
+
};
|
|
61
|
+
const validateClusterRelayVersion = (valueArg, pathArg) => (typeof valueArg === 'string'
|
|
62
|
+
&& valueArg.length <= maximumClusterRelayVersionLength
|
|
63
|
+
&& /^[0-9A-Za-z][0-9A-Za-z.+_-]*$/.test(valueArg)
|
|
64
|
+
? []
|
|
65
|
+
: [`${pathArg} must be 1 to ${maximumClusterRelayVersionLength} characters of letters, `
|
|
66
|
+
+ 'digits, dot, plus, underscore or hyphen, starting with a letter or digit']);
|
|
67
|
+
/** The same node id rule a node's own runtime session registers under. */
|
|
68
|
+
const validateClusterRelayNodeId = (valueArg, pathArg) => (typeof valueArg === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(valueArg)
|
|
69
|
+
? []
|
|
70
|
+
: [`${pathArg} must be 1 to 128 characters of letters, digits, underscore or hyphen`]);
|
|
71
|
+
const validateClusterRelayListenAddress = (valueArg, pathArg) => {
|
|
72
|
+
if (!isPlausibleUnicastIpv4(valueArg)) {
|
|
73
|
+
return [`${pathArg} must be a unicast IPv4 literal`];
|
|
74
|
+
}
|
|
75
|
+
// A relay that bound one of these can never serve the nodes of its cluster, and publishing
|
|
76
|
+
// it would put an unreachable address into every node's relay origin.
|
|
77
|
+
if (valueArg.startsWith('127.')) {
|
|
78
|
+
return [`${pathArg} must not be a loopback address`];
|
|
79
|
+
}
|
|
80
|
+
if (valueArg.startsWith('169.254.')) {
|
|
81
|
+
return [`${pathArg} must not be a link-local address`];
|
|
82
|
+
}
|
|
83
|
+
return [];
|
|
84
|
+
};
|
|
85
|
+
const validateClusterRelayListenPort = (valueArg, pathArg) => (typeof valueArg === 'number' && Number.isSafeInteger(valueArg) && valueArg >= 1 && valueArg <= 65535
|
|
86
|
+
? []
|
|
87
|
+
: [`${pathArg} must be an integer from 1 to 65535`]);
|
|
88
|
+
const validateClusterRelayEndpoint = (valueArg, pathArg) => {
|
|
89
|
+
if (!isRecord(valueArg)
|
|
90
|
+
|| !hasExactKeys(valueArg, ['version', 'nodeId', 'listenAddress', 'listenPort'])) {
|
|
91
|
+
return [`${pathArg} must use its exact schema`];
|
|
92
|
+
}
|
|
93
|
+
return [
|
|
94
|
+
...validateClusterRelayVersion(valueArg.version, `${pathArg} version`),
|
|
95
|
+
...validateClusterRelayNodeId(valueArg.nodeId, `${pathArg} nodeId`),
|
|
96
|
+
...validateClusterRelayListenAddress(valueArg.listenAddress, `${pathArg} listenAddress`),
|
|
97
|
+
...validateClusterRelayListenPort(valueArg.listenPort, `${pathArg} listenPort`),
|
|
98
|
+
];
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Validates one cluster relay registration and names every reason it is refused.
|
|
102
|
+
*
|
|
103
|
+
* Shape only, exactly like every other validator here: which cluster the relay speaks for comes
|
|
104
|
+
* from the verified identity on its socket, whether `relay.nodeId` is a node of that cluster is
|
|
105
|
+
* Cloudly's to decide against its own records, and whether this registration supersedes a live
|
|
106
|
+
* one is Cloudly's comparison of `registrationSequence` between peers. A registration that
|
|
107
|
+
* passes here has claimed nothing.
|
|
108
|
+
*/
|
|
109
|
+
export const validateClusterRelayRegistrationV1 = (valueArg, pathArg = 'cluster relay registration') => {
|
|
110
|
+
if (!isRecord(valueArg)
|
|
111
|
+
|| !hasExactKeys(valueArg, ['schemaVersion', 'relay', 'registrationSequence'])) {
|
|
112
|
+
return [`${pathArg} must use its exact schema`];
|
|
113
|
+
}
|
|
114
|
+
const errors = [];
|
|
115
|
+
if (valueArg.schemaVersion !== 1) {
|
|
116
|
+
errors.push(`${pathArg} schemaVersion must be 1`);
|
|
117
|
+
}
|
|
118
|
+
errors.push(...validateClusterRelayEndpoint(valueArg.relay, `${pathArg} relay`));
|
|
119
|
+
if (typeof valueArg.registrationSequence !== 'number'
|
|
120
|
+
|| !Number.isSafeInteger(valueArg.registrationSequence)
|
|
121
|
+
|| valueArg.registrationSequence < 0) {
|
|
122
|
+
errors.push(`${pathArg} registrationSequence must be a non-negative safe integer`);
|
|
123
|
+
}
|
|
124
|
+
return errors;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* The name the relay's certificate is issued for, and the A record Cloudly
|
|
128
|
+
* owns for it. Derived from the origin so every consumer derives it
|
|
129
|
+
* identically; an origin that names an address literal instead of a domain
|
|
130
|
+
* therefore has no issuable certificate.
|
|
131
|
+
*/
|
|
132
|
+
export const clusterRelayCertificateDomainName = (relayArg) => {
|
|
133
|
+
const errors = validateClusterRelayV1(relayArg);
|
|
134
|
+
if (errors.length > 0) {
|
|
135
|
+
throw new Error(errors[0]);
|
|
136
|
+
}
|
|
137
|
+
return new URL(relayArg.origin).hostname;
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2x1c3Rlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2RhdGEvY2x1c3Rlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUV6QyxPQUFPLEVBQTRCLE1BQU0sbUJBQW1CLENBQUM7QUFFN0QsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUF1RzVELE1BQU0sK0JBQStCLEdBQUcsSUFBSSxDQUFDO0FBQzdDLE1BQU0sZ0NBQWdDLEdBQUcsRUFBRSxDQUFDO0FBRTVDLE1BQU0sUUFBUSxHQUFHLENBQUMsUUFBaUIsRUFBdUMsRUFBRSxDQUFDLENBQzNFLE9BQU8sQ0FBQyxRQUFRLENBQUM7T0FDZCxPQUFPLFFBQVEsS0FBSyxRQUFRO09BQzVCLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUM7T0FDeEIsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLFFBQVEsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxTQUFTO1dBQ25ELE1BQU0sQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLEtBQUssSUFBSSxDQUFDLENBQy9DLENBQUM7QUFFRixNQUFNLFlBQVksR0FBRyxDQUNuQixRQUFpQyxFQUNqQyxPQUEwQixFQUNqQixFQUFFO0lBQ1gsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNoRCxNQUFNLFlBQVksR0FBRyxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDekMsT0FBTyxVQUFVLENBQUMsTUFBTSxLQUFLLFlBQVksQ0FBQyxNQUFNO1dBQzNDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxNQUFNLEVBQUUsUUFBUSxFQUFFLEVBQUUsQ0FBQyxNQUFNLEtBQUssWUFBWSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7QUFDakYsQ0FBQyxD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
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isPlausibleUnicastIpv4 } from '../private/ipv4.js';
|
|
1
2
|
export const privateNetworkV1Contract = Object.freeze({
|
|
2
3
|
privateRoot: 'internal.',
|
|
3
4
|
maximumAttachments: 16,
|
|
@@ -71,10 +72,7 @@ const sorted = (values) => {
|
|
|
71
72
|
return values;
|
|
72
73
|
};
|
|
73
74
|
const ipv4 = (value) => {
|
|
74
|
-
requireValue(
|
|
75
|
-
const parts = value.split('.');
|
|
76
|
-
requireValue(parts.length === 4 && parts.every(part => /^(?:0|[1-9][0-9]{0,2})$/.test(part) && Number(part) <= 255));
|
|
77
|
-
requireValue(Number(parts[0]) > 0 && Number(parts[0]) < 224 && value !== '255.255.255.255');
|
|
75
|
+
requireValue(isPlausibleUnicastIpv4(value));
|
|
78
76
|
return value;
|
|
79
77
|
};
|
|
80
78
|
const publicSuffix = (value) => {
|
|
@@ -221,4 +219,4 @@ export const composeServicePrivateNetworkDnsPolicyV1 = (membershipValue, network
|
|
|
221
219
|
return { organizationId: member.organizationId, serviceId: member.serviceId, membershipRevision: member.revision,
|
|
222
220
|
networkSuffixes, searchDomains, externalDns };
|
|
223
221
|
});
|
|
224
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJpdmF0ZW5ldHdvcmsuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL3ByaXZhdGVuZXR3b3JrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQW1FQSxNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ3BELFdBQVcsRUFBRSxXQUFXO0lBQ3hCLGtCQUFrQixFQUFFLEVBQUU7SUFDdEIsMkJBQTJCLEVBQUUsRUFBRTtJQUMvQixnQkFBZ0IsRUFBRSxDQUFDO0lBQ25CLHdCQUF3QixFQUFFLEVBQUU7SUFDNUIsaUJBQWlCLEVBQUUsT0FBTztJQUMxQix5QkFBeUIsRUFBRSxDQUFDO0lBQzVCLHlCQUF5QixFQUFFLENBQUM7SUFDNUIsdUJBQXVCLEVBQUUsMkNBQTJDO0lBQ3BFLFFBQVEsRUFBRSxXQUFXO0NBQ2IsQ0FBQyxDQUFDO0FBRVosTUFBTSxJQUFJLEdBQUcsR0FBVSxFQUFFLEdBQUcsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3JGLFNBQVMsWUFBWSxDQUFDLEtBQWMsSUFBbUIsSUFBSSxDQUFDLEtBQUs7SUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDNUUsTUFBTSxJQUFJLEdBQUcsQ0FBSSxTQUFrQixFQUFLLEVBQUUsR0FBRyxJQUFJLENBQUM7SUFBQyxPQUFPLFNBQVMsRUFBRSxDQUFDO0FBQUMsQ0FBQztBQUFDLE1BQU0sQ0FBQztJQUFDLE9BQU8sSUFBSSxFQUFFLENBQUM7QUFBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3BHLE1BQU0sTUFBTSxHQUFHLENBQUMsS0FBYyxFQUFFLElBQWMsRUFBMkIsRUFBRTtJQUN6RSxZQUFZLENBQUMsS0FBSyxLQUFLLElBQUksSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7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
|
|
222
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJpdmF0ZW5ldHdvcmsuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL3ByaXZhdGVuZXR3b3JrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBcUU1RCxNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ3BELFdBQVcsRUFBRSxXQUFXO0lBQ3hCLGtCQUFrQixFQUFFLEVBQUU7SUFDdEIsMkJBQTJCLEVBQUUsRUFBRTtJQUMvQixnQkFBZ0IsRUFBRSxDQUFDO0lBQ25CLHdCQUF3QixFQUFFLEVBQUU7SUFDNUIsaUJBQWlCLEVBQUUsT0FBTztJQUMxQix5QkFBeUIsRUFBRSxDQUFDO0lBQzVCLHlCQUF5QixFQUFFLENBQUM7SUFDNUIsdUJBQXVCLEVBQUUsMkNBQTJDO0lBQ3BFLFFBQVEsRUFBRSxXQUFXO0NBQ2IsQ0FBQyxDQUFDO0FBRVosTUFBTSxJQUFJLEdBQUcsR0FBVSxFQUFFLEdBQUcsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3JGLFNBQVMsWUFBWSxDQUFDLEtBQWMsSUFBbUIsSUFBSSxDQUFDLEtBQUs7SUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDNUUsTUFBTSxJQUFJLEdBQUcsQ0FBSSxTQUFrQixFQUFLLEVBQUUsR0FBRyxJQUFJLENBQUM7SUFBQyxPQUFPLFNBQVMsRUFBRSxDQUFDO0FBQUMsQ0FBQztBQUFDLE1BQU0sQ0FBQztJQUFDLE9BQU8sSUFBSSxFQUFFLENBQUM7QUFBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3BHLE1BQU0sTUFBTSxHQUFHLENBQUMsS0FBYyxFQUFFLElBQWMsRUFBMkIsRUFBRTtJQUN6RSxZQUFZLENBQUMsS0FBSyxLQUFLLElBQUksSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUks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
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dotted-quad IPv4 form every consumer of this package agrees on.
|
|
3
|
+
*
|
|
4
|
+
* Shape only: an address that passes here is still neither routable, owned, reachable nor
|
|
5
|
+
* authorized. The rule refuses what can never be a unicast host address — a quad that is not
|
|
6
|
+
* canonical, the `0.0.0.0/8` unspecified block, and everything from `224.0.0.0` up, which is
|
|
7
|
+
* multicast and reserved space and therefore also covers the all-ones broadcast address.
|
|
8
|
+
*/
|
|
9
|
+
export declare const isPlausibleUnicastIpv4: (valueArg: unknown) => valueArg is string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dotted-quad IPv4 form every consumer of this package agrees on.
|
|
3
|
+
*
|
|
4
|
+
* Shape only: an address that passes here is still neither routable, owned, reachable nor
|
|
5
|
+
* authorized. The rule refuses what can never be a unicast host address — a quad that is not
|
|
6
|
+
* canonical, the `0.0.0.0/8` unspecified block, and everything from `224.0.0.0` up, which is
|
|
7
|
+
* multicast and reserved space and therefore also covers the all-ones broadcast address.
|
|
8
|
+
*/
|
|
9
|
+
export const isPlausibleUnicastIpv4 = (valueArg) => {
|
|
10
|
+
if (typeof valueArg !== 'string') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const octets = valueArg.split('.');
|
|
14
|
+
if (octets.length !== 4) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (!octets.every((octetArg) => (/^(?:0|[1-9][0-9]{0,2})$/.test(octetArg) && Number(octetArg) <= 255))) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const leadingOctet = Number(octets[0]);
|
|
21
|
+
return leadingOctet > 0 && leadingOctet < 224;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXB2NC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3ByaXZhdGUvaXB2NC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsQ0FBQyxRQUFpQixFQUFzQixFQUFFO0lBQzlFLElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDakMsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBQ0QsTUFBTSxNQUFNLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNuQyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDeEIsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQzlCLHlCQUF5QixDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksR0FBRyxDQUNwRSxDQUFDLEVBQUUsQ0FBQztRQUNILE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUNELE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN2QyxPQUFPLFlBQVksR0FBRyxDQUFDLElBQUksWUFBWSxHQUFHLEdBQUcsQ0FBQztBQUNoRCxDQUFDLENBQUMifQ==
|
|
@@ -48,6 +48,53 @@ export interface IReq_Any_Cloudly_UpdateCluster extends plugins.typedrequestInte
|
|
|
48
48
|
resultCluster: clusterInterfaces.ICluster;
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Enables this cluster's relay.
|
|
53
|
+
*
|
|
54
|
+
* Cloudly derives the relay origin itself from the cluster and its own public hostname, so a
|
|
55
|
+
* caller cannot name one, and refuses the request when no managed DNS zone matches that name.
|
|
56
|
+
* The response carries the block Cloudly published, which is the same block every cluster-side
|
|
57
|
+
* component then reads from `ICluster.data.relay`.
|
|
58
|
+
*/
|
|
59
|
+
export interface IReq_Any_Cloudly_EnableClusterRelayV1 extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_EnableClusterRelayV1> {
|
|
60
|
+
method: 'enableClusterRelayV1';
|
|
61
|
+
request: {
|
|
62
|
+
identity: userInterfaces.IIdentity;
|
|
63
|
+
clusterId: string;
|
|
64
|
+
};
|
|
65
|
+
response: {
|
|
66
|
+
relay: clusterInterfaces.IClusterRelayV1;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Disables this cluster's relay, removing the block from the cluster document.
|
|
71
|
+
*
|
|
72
|
+
* The nodes of the cluster target Cloudly directly again, so `relay` is explicitly `null` in
|
|
73
|
+
* the response rather than absent: the caller learns that the block is gone, not that the
|
|
74
|
+
* response forgot it.
|
|
75
|
+
*/
|
|
76
|
+
export interface IReq_Any_Cloudly_DisableClusterRelayV1 extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_DisableClusterRelayV1> {
|
|
77
|
+
method: 'disableClusterRelayV1';
|
|
78
|
+
request: {
|
|
79
|
+
identity: userInterfaces.IIdentity;
|
|
80
|
+
clusterId: string;
|
|
81
|
+
};
|
|
82
|
+
response: {
|
|
83
|
+
relay: null;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The cluster relay tells Cloudly where it listens, over its own authenticated socket.
|
|
88
|
+
*
|
|
89
|
+
* Sent after `registerCloudlyClientSessionV1`, so the cluster is the one the verified machine
|
|
90
|
+
* identity on that socket names and is never a request field. `Relay` names the role, not a
|
|
91
|
+
* component: whichever process fills it for a cluster sends this.
|
|
92
|
+
*/
|
|
93
|
+
export interface IReq_Relay_Cloudly_RegisterClusterRelayV1 extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Relay_Cloudly_RegisterClusterRelayV1> {
|
|
94
|
+
method: 'registerClusterRelayV1';
|
|
95
|
+
request: clusterInterfaces.IClusterRelayRegistrationV1;
|
|
96
|
+
response: clusterInterfaces.IClusterRelayRegistrationResponseV1;
|
|
97
|
+
}
|
|
51
98
|
/**
|
|
52
99
|
* deletes a cluster
|
|
53
100
|
*/
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -523,7 +523,9 @@ Existing numeric configurations retain their exact meaning and digest.
|
|
|
523
523
|
`requests.runtimesession` defines `registerPalletRuntimeSessionV1`,
|
|
524
524
|
`applyRuntimeAssignmentV1`, `reportRuntimeAssignmentObservationV1`, and
|
|
525
525
|
`getRuntimeRegistryCredentialV1`. Pallet initiates WSS through its configured
|
|
526
|
-
Cloudly HTTPS origin
|
|
526
|
+
Cloudly HTTPS origin, or, on a relayed cluster, through the relay origin its
|
|
527
|
+
cluster publishes in [`ICluster.data.relay`](#cluster-relay-block) while Cloudly
|
|
528
|
+
remains the credential authority. The receiver derives the physical peer, process instance
|
|
527
529
|
and registration idempotency; callers cannot supply those authorities. A serialized
|
|
528
530
|
`IRuntimeSessionBindingV1` provides content to compare with authenticated peer
|
|
529
531
|
state and does not itself authenticate a connection. Registry credentials are
|
|
@@ -1054,7 +1056,8 @@ const service: data.IService = {
|
|
|
1054
1056
|
|
|
1055
1057
|
Common data contracts include:
|
|
1056
1058
|
|
|
1057
|
-
- `ICluster` and `IClusterNode` for cluster membership and provisioning state
|
|
1059
|
+
- `ICluster` and `IClusterNode` for cluster membership and provisioning state, including the
|
|
1060
|
+
optional `ICluster.data.relay` block described below.
|
|
1058
1061
|
- `IService`, `IDeployment`, `IImage`, `IRegistryTarget`, and `IExternalRegistry` for workload delivery.
|
|
1059
1062
|
- Service port contracts including `IServiceTargetPort`, `IServiceDomainRoute`, and `IServicePublicPortMapping` for canonical backend targets, domain target references, and edge/Coretraffic TCP/UDP public exposure.
|
|
1060
1063
|
- `IDomain`, `IDnsEntry`, and traffic contracts for routing and DNS management.
|
|
@@ -1080,6 +1083,77 @@ Common data contracts include:
|
|
|
1080
1083
|
- `IUser`, JWT-only `IIdentityCredential`, full `IIdentity`, and token-related contracts for authentication context. `IIdentity` extends `IIdentityCredential` with server-issued user metadata.
|
|
1081
1084
|
- `ICloudlyConfig`, `ICloudlySettings`, status, server, bare-metal, BaseOS, backup, and task execution interfaces for control-plane state.
|
|
1082
1085
|
|
|
1086
|
+
### Cluster Relay Block
|
|
1087
|
+
|
|
1088
|
+
`ICluster.data.relay` is the optional `IClusterRelayV1 { schemaVersion: 1; origin: string }`
|
|
1089
|
+
that states how cluster-side components reach this cluster's relay. It is absent on a cluster
|
|
1090
|
+
whose nodes still target Cloudly directly, so its presence is what marks a cluster as relayed.
|
|
1091
|
+
|
|
1092
|
+
`origin` is the exact value a Pallet node stores as its `relayOrigin` — the origin its runtime
|
|
1093
|
+
session registers over, as described in [Authenticated Runtime Sessions](#authenticated-runtime-sessions).
|
|
1094
|
+
It must be an `https:` origin with no path, query, fragment, userinfo or trailing slash, so that
|
|
1095
|
+
`new URL(origin).origin === origin`; an explicit port is allowed, and because `URL` drops a
|
|
1096
|
+
default port and lowercases the host, `https://relay.example:443` and `https://Relay.example`
|
|
1097
|
+
are not canonical origins. `validateClusterRelayV1(value, path?)` returns one named reason per
|
|
1098
|
+
violation and an empty array for a valid block.
|
|
1099
|
+
|
|
1100
|
+
The relay's certificate subject is not a second field: every consumer derives it from the origin
|
|
1101
|
+
with `clusterRelayCertificateDomainName(relay)`, which returns the origin's hostname — the name
|
|
1102
|
+
Cloudly issues the certificate for and owns the A record for. An origin naming an address literal
|
|
1103
|
+
therefore has no issuable certificate.
|
|
1104
|
+
|
|
1105
|
+
```typescript
|
|
1106
|
+
const relay: data.IClusterRelayV1 = {
|
|
1107
|
+
schemaVersion: 1,
|
|
1108
|
+
origin: 'https://relay.cluster-a.serve.zone:8443',
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
const reasons = data.validateClusterRelayV1(relay); // []
|
|
1112
|
+
const certificateDomainName = data.clusterRelayCertificateDomainName(relay);
|
|
1113
|
+
// 'relay.cluster-a.serve.zone'
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
Enabling a relay is one operator flip per cluster. `enableClusterRelayV1 { identity, clusterId }`
|
|
1117
|
+
answers with the block Cloudly published, and `disableClusterRelayV1` removes it and answers with
|
|
1118
|
+
an explicit `relay: null`. The caller names only the cluster: Cloudly derives the origin itself
|
|
1119
|
+
from that cluster and its own public hostname, and refuses the request when no DNS zone it
|
|
1120
|
+
manages matches the derived name, so a relay name that nothing can publish is never written.
|
|
1121
|
+
|
|
1122
|
+
Once its socket is authenticated with `registerCloudlyClientSessionV1`, the relay sends
|
|
1123
|
+
`registerClusterRelayV1` carrying `IClusterRelayRegistrationV1`: the relay build, the cluster node
|
|
1124
|
+
it runs on, the exact endpoint it bound for its cluster's Pallet nodes, and a
|
|
1125
|
+
`registrationSequence` that counts up once per registration within one relay process and restarts
|
|
1126
|
+
at `0` when that process restarts. `validateClusterRelayRegistrationV1(value, path?)` returns one
|
|
1127
|
+
named reason per violation, like every validator here, and checks shape only. `listenAddress` is
|
|
1128
|
+
an IPv4 literal because Cloudly publishes it verbatim as the A record of the relay's certificate
|
|
1129
|
+
domain name — a hostname would need a `CNAME` and an IPv6 address an `AAAA` record, so admitting
|
|
1130
|
+
either is an additive change once Cloudly publishes it. A loopback or link-local address is
|
|
1131
|
+
refused by its own name, because a relay that bound one can never serve the nodes of its cluster.
|
|
1132
|
+
|
|
1133
|
+
Authority stays with Cloudly, and none of it is a request field. The cluster is the one the
|
|
1134
|
+
verified machine identity on that socket names; whether `relay.nodeId` is a node of that cluster
|
|
1135
|
+
is decided against Cloudly's own records; and one cluster has one relay, so when two peers are
|
|
1136
|
+
live the one holding the highest accepted `registrationSequence` is the relay and the other is
|
|
1137
|
+
never selected. The response carries the origin Cloudly published, or `null` while the cluster
|
|
1138
|
+
carries no relay block, which a relay must read as "not enabled yet" rather than as licence to
|
|
1139
|
+
invent an origin: it is how the relay checks that the certificate it holds and the name its nodes
|
|
1140
|
+
are told to reach are the same one.
|
|
1141
|
+
|
|
1142
|
+
```typescript
|
|
1143
|
+
const registration: data.IClusterRelayRegistrationV1 = {
|
|
1144
|
+
schemaVersion: 1,
|
|
1145
|
+
relay: {
|
|
1146
|
+
version: '4.0.0',
|
|
1147
|
+
nodeId: 'node-a',
|
|
1148
|
+
listenAddress: '10.0.4.7',
|
|
1149
|
+
listenPort: 8443,
|
|
1150
|
+
},
|
|
1151
|
+
registrationSequence: 0,
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
const registrationReasons = data.validateClusterRelayRegistrationV1(registration); // []
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1083
1157
|
Shared service port helpers are exported from `data` so Cloudly, App Store resolution, Coreflow, Coretraffic, Onebox, and dcrouter agree on the same normalization rules:
|
|
1084
1158
|
|
|
1085
1159
|
```typescript
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/data/cluster.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as plugins from '../plugins.js';
|
|
|
2
2
|
|
|
3
3
|
import { type IDockerRegistryInfo } from '../data/docker.js';
|
|
4
4
|
import type { IClusterNode } from './clusternode.js';
|
|
5
|
+
import { isPlausibleUnicastIpv4 } from '../private/ipv4.js';
|
|
5
6
|
|
|
6
7
|
export interface ICluster {
|
|
7
8
|
id: string;
|
|
@@ -37,5 +38,234 @@ export interface ICluster {
|
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
sshKeys: plugins.tsclass.network.ISshKey[];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* How cluster-side components reach this cluster's relay. Absent on a
|
|
44
|
+
* cluster whose nodes still target Cloudly directly.
|
|
45
|
+
*/
|
|
46
|
+
relay?: IClusterRelayV1;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* How cluster-side components reach this cluster's relay.
|
|
52
|
+
*
|
|
53
|
+
* `origin` is the exact value a Pallet node stores as its `relayOrigin`: an
|
|
54
|
+
* `https:` origin, an explicit port allowed, with no path, no query, no
|
|
55
|
+
* fragment, no userinfo and no trailing slash, so that
|
|
56
|
+
* `new URL(origin).origin === origin`. A default `:443` is therefore written
|
|
57
|
+
* implicitly, because `URL` drops it from the origin it computes.
|
|
58
|
+
*/
|
|
59
|
+
export interface IClusterRelayV1 {
|
|
60
|
+
schemaVersion: 1;
|
|
61
|
+
origin: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* What a cluster relay tells Cloudly about itself once its socket is authenticated.
|
|
66
|
+
*
|
|
67
|
+
* The relay sends this after `registerCloudlyClientSessionV1`, so the cluster it registers for
|
|
68
|
+
* is the one its verified machine identity names and is never a field here. `relay.nodeId` is
|
|
69
|
+
* the cluster node the relay process runs on, `listenAddress` and `listenPort` are the exact
|
|
70
|
+
* endpoint it bound for the Pallet nodes of its cluster, and `registrationSequence` counts up
|
|
71
|
+
* once per registration within one relay process, so Cloudly can tell a newer registration from
|
|
72
|
+
* a replayed older one while both peers are live.
|
|
73
|
+
*/
|
|
74
|
+
export interface IClusterRelayRegistrationV1 {
|
|
75
|
+
schemaVersion: 1;
|
|
76
|
+
relay: {
|
|
77
|
+
/** The relay build that is registering; a bounded label for operators and logs. */
|
|
78
|
+
version: string;
|
|
79
|
+
nodeId: string;
|
|
80
|
+
/**
|
|
81
|
+
* The address the relay bound, as an IPv4 literal.
|
|
82
|
+
*
|
|
83
|
+
* Cloudly publishes this value verbatim as the A record of the relay's certificate domain
|
|
84
|
+
* name, so the contract admits exactly what that record can carry: a hostname would need a
|
|
85
|
+
* `CNAME` and an IPv6 address an `AAAA` record, and neither is published today. Admitting
|
|
86
|
+
* either is an additive change once Cloudly publishes it.
|
|
87
|
+
*/
|
|
88
|
+
listenAddress: string;
|
|
89
|
+
listenPort: number;
|
|
40
90
|
};
|
|
91
|
+
/** Monotonic within one relay process; it restarts at 0 when the process restarts. */
|
|
92
|
+
registrationSequence: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* What Cloudly tells the relay back.
|
|
97
|
+
*
|
|
98
|
+
* `origin` is the origin Cloudly has published for this cluster, so a relay can check that the
|
|
99
|
+
* certificate it holds and the name its nodes are told to reach are the same one. It is `null`
|
|
100
|
+
* while the cluster carries no relay block, which a relay must treat as "not enabled yet"
|
|
101
|
+
* rather than as an origin it may invent.
|
|
102
|
+
*/
|
|
103
|
+
export interface IClusterRelayRegistrationResponseV1 {
|
|
104
|
+
accepted: true;
|
|
105
|
+
origin: string | null;
|
|
41
106
|
}
|
|
107
|
+
|
|
108
|
+
const maximumClusterRelayOriginLength = 2048;
|
|
109
|
+
const maximumClusterRelayVersionLength = 64;
|
|
110
|
+
|
|
111
|
+
const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => (
|
|
112
|
+
Boolean(valueArg)
|
|
113
|
+
&& typeof valueArg === 'object'
|
|
114
|
+
&& !Array.isArray(valueArg)
|
|
115
|
+
&& (Object.getPrototypeOf(valueArg) === Object.prototype
|
|
116
|
+
|| Object.getPrototypeOf(valueArg) === null)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const hasExactKeys = (
|
|
120
|
+
valueArg: Record<string, unknown>,
|
|
121
|
+
keysArg: readonly string[],
|
|
122
|
+
): boolean => {
|
|
123
|
+
const actualKeys = Object.keys(valueArg).sort();
|
|
124
|
+
const expectedKeys = [...keysArg].sort();
|
|
125
|
+
return actualKeys.length === expectedKeys.length
|
|
126
|
+
&& actualKeys.every((keyArg, indexArg) => keyArg === expectedKeys[indexArg]);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const validateClusterRelayOrigin = (valueArg: unknown, pathArg: string): string[] => {
|
|
130
|
+
if (typeof valueArg !== 'string'
|
|
131
|
+
|| valueArg.length === 0
|
|
132
|
+
|| valueArg.length > maximumClusterRelayOriginLength) {
|
|
133
|
+
return [`${pathArg} must be a string of 1 to ${maximumClusterRelayOriginLength} characters`];
|
|
134
|
+
}
|
|
135
|
+
let parsedOrigin: URL;
|
|
136
|
+
try {
|
|
137
|
+
parsedOrigin = new URL(valueArg);
|
|
138
|
+
} catch {
|
|
139
|
+
return [`${pathArg} must be a parsable URL`];
|
|
140
|
+
}
|
|
141
|
+
const errors: string[] = [];
|
|
142
|
+
if (parsedOrigin.protocol !== 'https:') {
|
|
143
|
+
errors.push(`${pathArg} must use the https scheme`);
|
|
144
|
+
}
|
|
145
|
+
if (parsedOrigin.username || parsedOrigin.password) {
|
|
146
|
+
errors.push(`${pathArg} must not carry userinfo`);
|
|
147
|
+
}
|
|
148
|
+
if (parsedOrigin.origin !== valueArg) {
|
|
149
|
+
errors.push(
|
|
150
|
+
`${pathArg} must be the canonical origin ${parsedOrigin.origin}, `
|
|
151
|
+
+ 'without a path, query, fragment, trailing slash or default port',
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return errors;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Validates one cluster relay block and names every reason it is refused.
|
|
159
|
+
*
|
|
160
|
+
* A relay whose origin cannot be trusted verbatim must never reach a node's
|
|
161
|
+
* identity store: it is the transport authority the node's runtime session
|
|
162
|
+
* registers over.
|
|
163
|
+
*/
|
|
164
|
+
export const validateClusterRelayV1 = (
|
|
165
|
+
valueArg: unknown,
|
|
166
|
+
pathArg = 'cluster relay',
|
|
167
|
+
): string[] => {
|
|
168
|
+
if (!isRecord(valueArg) || !hasExactKeys(valueArg, ['schemaVersion', 'origin'])) {
|
|
169
|
+
return [`${pathArg} must use its exact schema`];
|
|
170
|
+
}
|
|
171
|
+
const errors: string[] = [];
|
|
172
|
+
if (valueArg.schemaVersion !== 1) {
|
|
173
|
+
errors.push(`${pathArg} schemaVersion must be 1`);
|
|
174
|
+
}
|
|
175
|
+
errors.push(...validateClusterRelayOrigin(valueArg.origin, `${pathArg} origin`));
|
|
176
|
+
return errors;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const validateClusterRelayVersion = (valueArg: unknown, pathArg: string): string[] => (
|
|
180
|
+
typeof valueArg === 'string'
|
|
181
|
+
&& valueArg.length <= maximumClusterRelayVersionLength
|
|
182
|
+
&& /^[0-9A-Za-z][0-9A-Za-z.+_-]*$/.test(valueArg)
|
|
183
|
+
? []
|
|
184
|
+
: [`${pathArg} must be 1 to ${maximumClusterRelayVersionLength} characters of letters, `
|
|
185
|
+
+ 'digits, dot, plus, underscore or hyphen, starting with a letter or digit']
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
/** The same node id rule a node's own runtime session registers under. */
|
|
189
|
+
const validateClusterRelayNodeId = (valueArg: unknown, pathArg: string): string[] => (
|
|
190
|
+
typeof valueArg === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(valueArg)
|
|
191
|
+
? []
|
|
192
|
+
: [`${pathArg} must be 1 to 128 characters of letters, digits, underscore or hyphen`]
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const validateClusterRelayListenAddress = (valueArg: unknown, pathArg: string): string[] => {
|
|
196
|
+
if (!isPlausibleUnicastIpv4(valueArg)) {
|
|
197
|
+
return [`${pathArg} must be a unicast IPv4 literal`];
|
|
198
|
+
}
|
|
199
|
+
// A relay that bound one of these can never serve the nodes of its cluster, and publishing
|
|
200
|
+
// it would put an unreachable address into every node's relay origin.
|
|
201
|
+
if (valueArg.startsWith('127.')) {
|
|
202
|
+
return [`${pathArg} must not be a loopback address`];
|
|
203
|
+
}
|
|
204
|
+
if (valueArg.startsWith('169.254.')) {
|
|
205
|
+
return [`${pathArg} must not be a link-local address`];
|
|
206
|
+
}
|
|
207
|
+
return [];
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const validateClusterRelayListenPort = (valueArg: unknown, pathArg: string): string[] => (
|
|
211
|
+
typeof valueArg === 'number' && Number.isSafeInteger(valueArg) && valueArg >= 1 && valueArg <= 65535
|
|
212
|
+
? []
|
|
213
|
+
: [`${pathArg} must be an integer from 1 to 65535`]
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const validateClusterRelayEndpoint = (valueArg: unknown, pathArg: string): string[] => {
|
|
217
|
+
if (!isRecord(valueArg)
|
|
218
|
+
|| !hasExactKeys(valueArg, ['version', 'nodeId', 'listenAddress', 'listenPort'])) {
|
|
219
|
+
return [`${pathArg} must use its exact schema`];
|
|
220
|
+
}
|
|
221
|
+
return [
|
|
222
|
+
...validateClusterRelayVersion(valueArg.version, `${pathArg} version`),
|
|
223
|
+
...validateClusterRelayNodeId(valueArg.nodeId, `${pathArg} nodeId`),
|
|
224
|
+
...validateClusterRelayListenAddress(valueArg.listenAddress, `${pathArg} listenAddress`),
|
|
225
|
+
...validateClusterRelayListenPort(valueArg.listenPort, `${pathArg} listenPort`),
|
|
226
|
+
];
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Validates one cluster relay registration and names every reason it is refused.
|
|
231
|
+
*
|
|
232
|
+
* Shape only, exactly like every other validator here: which cluster the relay speaks for comes
|
|
233
|
+
* from the verified identity on its socket, whether `relay.nodeId` is a node of that cluster is
|
|
234
|
+
* Cloudly's to decide against its own records, and whether this registration supersedes a live
|
|
235
|
+
* one is Cloudly's comparison of `registrationSequence` between peers. A registration that
|
|
236
|
+
* passes here has claimed nothing.
|
|
237
|
+
*/
|
|
238
|
+
export const validateClusterRelayRegistrationV1 = (
|
|
239
|
+
valueArg: unknown,
|
|
240
|
+
pathArg = 'cluster relay registration',
|
|
241
|
+
): string[] => {
|
|
242
|
+
if (!isRecord(valueArg)
|
|
243
|
+
|| !hasExactKeys(valueArg, ['schemaVersion', 'relay', 'registrationSequence'])) {
|
|
244
|
+
return [`${pathArg} must use its exact schema`];
|
|
245
|
+
}
|
|
246
|
+
const errors: string[] = [];
|
|
247
|
+
if (valueArg.schemaVersion !== 1) {
|
|
248
|
+
errors.push(`${pathArg} schemaVersion must be 1`);
|
|
249
|
+
}
|
|
250
|
+
errors.push(...validateClusterRelayEndpoint(valueArg.relay, `${pathArg} relay`));
|
|
251
|
+
if (typeof valueArg.registrationSequence !== 'number'
|
|
252
|
+
|| !Number.isSafeInteger(valueArg.registrationSequence)
|
|
253
|
+
|| valueArg.registrationSequence < 0) {
|
|
254
|
+
errors.push(`${pathArg} registrationSequence must be a non-negative safe integer`);
|
|
255
|
+
}
|
|
256
|
+
return errors;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* The name the relay's certificate is issued for, and the A record Cloudly
|
|
261
|
+
* owns for it. Derived from the origin so every consumer derives it
|
|
262
|
+
* identically; an origin that names an address literal instead of a domain
|
|
263
|
+
* therefore has no issuable certificate.
|
|
264
|
+
*/
|
|
265
|
+
export const clusterRelayCertificateDomainName = (relayArg: IClusterRelayV1): string => {
|
|
266
|
+
const errors = validateClusterRelayV1(relayArg);
|
|
267
|
+
if (errors.length > 0) {
|
|
268
|
+
throw new Error(errors[0]);
|
|
269
|
+
}
|
|
270
|
+
return new URL(relayArg.origin).hostname;
|
|
271
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isPlausibleUnicastIpv4 } from '../private/ipv4.js';
|
|
2
|
+
|
|
1
3
|
/** Cloudly owns these definitions. Membership never follows node placement. */
|
|
2
4
|
export interface IPrivateNetworkPolicyV1 {
|
|
3
5
|
connectivity: 'member-full-mesh';
|
|
@@ -133,10 +135,7 @@ const sorted = (values: string[]): string[] => {
|
|
|
133
135
|
return values;
|
|
134
136
|
};
|
|
135
137
|
const ipv4 = (value: unknown): string => {
|
|
136
|
-
requireValue(
|
|
137
|
-
const parts = value.split('.');
|
|
138
|
-
requireValue(parts.length === 4 && parts.every(part => /^(?:0|[1-9][0-9]{0,2})$/.test(part) && Number(part) <= 255));
|
|
139
|
-
requireValue(Number(parts[0]) > 0 && Number(parts[0]) < 224 && value !== '255.255.255.255');
|
|
138
|
+
requireValue(isPlausibleUnicastIpv4(value));
|
|
140
139
|
return value;
|
|
141
140
|
};
|
|
142
141
|
const publicSuffix = (value: unknown): string => {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dotted-quad IPv4 form every consumer of this package agrees on.
|
|
3
|
+
*
|
|
4
|
+
* Shape only: an address that passes here is still neither routable, owned, reachable nor
|
|
5
|
+
* authorized. The rule refuses what can never be a unicast host address — a quad that is not
|
|
6
|
+
* canonical, the `0.0.0.0/8` unspecified block, and everything from `224.0.0.0` up, which is
|
|
7
|
+
* multicast and reserved space and therefore also covers the all-ones broadcast address.
|
|
8
|
+
*/
|
|
9
|
+
export const isPlausibleUnicastIpv4 = (valueArg: unknown): valueArg is string => {
|
|
10
|
+
if (typeof valueArg !== 'string') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const octets = valueArg.split('.');
|
|
14
|
+
if (octets.length !== 4) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (!octets.every((octetArg) => (
|
|
18
|
+
/^(?:0|[1-9][0-9]{0,2})$/.test(octetArg) && Number(octetArg) <= 255
|
|
19
|
+
))) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const leadingOctet = Number(octets[0]);
|
|
23
|
+
return leadingOctet > 0 && leadingOctet < 224;
|
|
24
|
+
};
|
package/ts/requests/cluster.ts
CHANGED
|
@@ -66,6 +66,65 @@ export interface IReq_Any_Cloudly_UpdateCluster extends plugins.typedrequestInte
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Enables this cluster's relay.
|
|
71
|
+
*
|
|
72
|
+
* Cloudly derives the relay origin itself from the cluster and its own public hostname, so a
|
|
73
|
+
* caller cannot name one, and refuses the request when no managed DNS zone matches that name.
|
|
74
|
+
* The response carries the block Cloudly published, which is the same block every cluster-side
|
|
75
|
+
* component then reads from `ICluster.data.relay`.
|
|
76
|
+
*/
|
|
77
|
+
export interface IReq_Any_Cloudly_EnableClusterRelayV1 extends plugins.typedrequestInterfaces.implementsTR<
|
|
78
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
79
|
+
IReq_Any_Cloudly_EnableClusterRelayV1
|
|
80
|
+
> {
|
|
81
|
+
method: 'enableClusterRelayV1';
|
|
82
|
+
request: {
|
|
83
|
+
identity: userInterfaces.IIdentity;
|
|
84
|
+
clusterId: string;
|
|
85
|
+
};
|
|
86
|
+
response: {
|
|
87
|
+
relay: clusterInterfaces.IClusterRelayV1;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Disables this cluster's relay, removing the block from the cluster document.
|
|
93
|
+
*
|
|
94
|
+
* The nodes of the cluster target Cloudly directly again, so `relay` is explicitly `null` in
|
|
95
|
+
* the response rather than absent: the caller learns that the block is gone, not that the
|
|
96
|
+
* response forgot it.
|
|
97
|
+
*/
|
|
98
|
+
export interface IReq_Any_Cloudly_DisableClusterRelayV1 extends plugins.typedrequestInterfaces.implementsTR<
|
|
99
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
100
|
+
IReq_Any_Cloudly_DisableClusterRelayV1
|
|
101
|
+
> {
|
|
102
|
+
method: 'disableClusterRelayV1';
|
|
103
|
+
request: {
|
|
104
|
+
identity: userInterfaces.IIdentity;
|
|
105
|
+
clusterId: string;
|
|
106
|
+
};
|
|
107
|
+
response: {
|
|
108
|
+
relay: null;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The cluster relay tells Cloudly where it listens, over its own authenticated socket.
|
|
114
|
+
*
|
|
115
|
+
* Sent after `registerCloudlyClientSessionV1`, so the cluster is the one the verified machine
|
|
116
|
+
* identity on that socket names and is never a request field. `Relay` names the role, not a
|
|
117
|
+
* component: whichever process fills it for a cluster sends this.
|
|
118
|
+
*/
|
|
119
|
+
export interface IReq_Relay_Cloudly_RegisterClusterRelayV1 extends plugins.typedrequestInterfaces.implementsTR<
|
|
120
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
121
|
+
IReq_Relay_Cloudly_RegisterClusterRelayV1
|
|
122
|
+
> {
|
|
123
|
+
method: 'registerClusterRelayV1';
|
|
124
|
+
request: clusterInterfaces.IClusterRelayRegistrationV1;
|
|
125
|
+
response: clusterInterfaces.IClusterRelayRegistrationResponseV1;
|
|
126
|
+
}
|
|
127
|
+
|
|
69
128
|
/**
|
|
70
129
|
* deletes a cluster
|
|
71
130
|
*/
|