@serve.zone/interfaces 32.40.0 → 32.41.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 +20 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/clusterrelaycredential.d.ts +72 -0
- package/dist_ts/data/clusterrelaycredential.js +82 -1
- package/dist_ts/requests/cluster.d.ts +34 -0
- package/package.json +2 -2
- package/readme.md +33 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/clusterrelaycredential.ts +149 -0
- package/ts/requests/cluster.ts +44 -0
package/changelog.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-10-02 - 32.41.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- An administrator can export a cluster relay's bearer without an exec into Cloudly's process.
|
|
8
|
+
`exportClusterRelayAuthorization { identity, clusterId, expectedGeneration }` answers
|
|
9
|
+
`IClusterRelayAuthorizationExport`: the sealed relay credential at exactly that generation, the
|
|
10
|
+
bearer it delivered (`authorization`) and the audit `receipt` Cloudly writes before it answers.
|
|
11
|
+
Cloudly mints nothing to answer it; a new bearer is a `rotate` followed by an export.
|
|
12
|
+
`getClusterRelayAuthorizationExports { identity, clusterId }` answers the value-free receipts.
|
|
13
|
+
`data.clusterRelayAuthorizationExportRefusals` names the refusals, and
|
|
14
|
+
`bindClusterRelayAuthorizationExport`, `isClusterRelayAuthorization` and the exact-keyed
|
|
15
|
+
snapshots let a reader check an answer before it writes the bearer, without ever quoting it.
|
|
16
|
+
|
|
17
|
+
## 2026-10-02 - 32.40.1
|
|
18
|
+
|
|
19
|
+
### Fixes
|
|
20
|
+
|
|
21
|
+
- Depend on `@push.rocks/smartcrypto` 3, which no longer ships node-forge, so consumers' production trees are free of node-forge advisory GHSA-86w9-cpqp-85rv (lenient RSA PKCS#1 v1.5 verification, no patched release). The contracts use only smartcrypto's X25519 envelope API, which is unchanged.
|
|
22
|
+
|
|
3
23
|
## 2026-10-02 - 32.40.0
|
|
4
24
|
|
|
5
25
|
### Features
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/interfaces',
|
|
6
|
-
version: '32.
|
|
6
|
+
version: '32.41.0',
|
|
7
7
|
description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
|
|
@@ -106,6 +106,50 @@ export interface IClusterLegacyRelayCredentialRetirementResult {
|
|
|
106
106
|
/** A replay returns the recorded invalidation; nothing is revoked twice. */
|
|
107
107
|
replayed: boolean;
|
|
108
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* What an administrator names to export one cluster's relay bearer: the cluster, at exactly the
|
|
111
|
+
* generation `getClusterRelayCredential` answered. Never a minimum: a rotation that committed
|
|
112
|
+
* after that read is refused rather than answered with a bearer the operator did not ask for.
|
|
113
|
+
*/
|
|
114
|
+
export interface IClusterRelayAuthorizationExportRequest {
|
|
115
|
+
clusterId: string;
|
|
116
|
+
expectedGeneration: number;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The audit record of one answered export, which Cloudly writes before it answers the bearer.
|
|
120
|
+
*
|
|
121
|
+
* Append-only and value-free: it names who received which credential's bearer and when, never
|
|
122
|
+
* the bearer or a hash of it. Every answered export writes one, so a retried export is a second
|
|
123
|
+
* receipt rather than a replay: each is a separate handover of the same bearer.
|
|
124
|
+
*/
|
|
125
|
+
export interface IClusterRelayAuthorizationExportReceipt {
|
|
126
|
+
id: string;
|
|
127
|
+
clusterId: string;
|
|
128
|
+
/** The exact relay credential whose bearer was answered: identity, generation and digest. */
|
|
129
|
+
credential: IRuntimeAuthorityReference;
|
|
130
|
+
/** The administrator the bearer was answered to, read from the verified credential. */
|
|
131
|
+
actorId: string;
|
|
132
|
+
exportedAt: number;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* One cluster's relay bearer, answered to an administrator so it can be handed to the relay
|
|
136
|
+
* without reaching into Cloudly's process.
|
|
137
|
+
*
|
|
138
|
+
* The only member of this contract that carries a bearer. It is the delivered version the
|
|
139
|
+
* credential names, at the requested generation, proved against the hash that generation issued;
|
|
140
|
+
* Cloudly mints nothing to answer it. The reader owns the bytes: it writes them where the relay
|
|
141
|
+
* reads them and never logs, prints or persists them anywhere else.
|
|
142
|
+
*/
|
|
143
|
+
export interface IClusterRelayAuthorizationExport {
|
|
144
|
+
/** The relay credential the bearer authenticates as, at exactly the requested generation. */
|
|
145
|
+
credential: IClusterRelayCredential;
|
|
146
|
+
/**
|
|
147
|
+
* The bearer, byte for byte what the relay presents: `clusterRelayToken_`, the credential's
|
|
148
|
+
* 64-hex `id`, `_` and 64 hex characters, with no line terminator.
|
|
149
|
+
*/
|
|
150
|
+
authorization: string;
|
|
151
|
+
receipt: IClusterRelayAuthorizationExportReceipt;
|
|
152
|
+
}
|
|
109
153
|
export declare const clusterRelayCredentialContract: Readonly<{
|
|
110
154
|
readonly credentialDigestDomain: "serve.zone/cluster-relay-credential";
|
|
111
155
|
readonly legacyCredentialDigestDomain: "serve.zone/cluster-relay-legacy-credential";
|
|
@@ -114,6 +158,12 @@ export declare const clusterRelayCredentialContract: Readonly<{
|
|
|
114
158
|
/** Every reason Cloudly answers instead of mutating or retiring a cluster relay authority. */
|
|
115
159
|
export declare const clusterRelayCredentialRefusals: readonly ["relay-credential-absent", "legacy-credential-absent", "legacy-credential-already-retired", "legacy-retirement-replacement-not-current", "legacy-retirement-registration-not-current", "legacy-retirement-relay-not-live", "conflict"];
|
|
116
160
|
export type TClusterRelayCredentialRefusal = typeof clusterRelayCredentialRefusals[number];
|
|
161
|
+
/**
|
|
162
|
+
* Every reason Cloudly answers instead of a relay bearer. Its own family, because an export
|
|
163
|
+
* changes no record: it has no `conflict`, and a generation it was not asked for is a mismatch.
|
|
164
|
+
*/
|
|
165
|
+
export declare const clusterRelayAuthorizationExportRefusals: readonly ["relay-credential-absent", "relay-credential-generation-mismatch", "relay-authorization-missing", "relay-authorization-ambiguous"];
|
|
166
|
+
export type TClusterRelayAuthorizationExportRefusal = typeof clusterRelayAuthorizationExportRefusals[number];
|
|
117
167
|
/** Structural copy only; whether the record is the cluster's current one is Cloudly's to decide. */
|
|
118
168
|
export declare const snapshotClusterRelayCredential: (valueArg: unknown) => IClusterRelayCredential;
|
|
119
169
|
export declare const computeClusterRelayCredentialDigest: (valueArg: unknown) => Promise<TSha256Digest>;
|
|
@@ -138,4 +188,26 @@ export declare const bindClusterLegacyRelayCredentialInvalidation: (invalidation
|
|
|
138
188
|
legacyCredential: IClusterLegacyRelayCredential;
|
|
139
189
|
acceptedRegistration: IClusterRelayAcceptedRegistration;
|
|
140
190
|
}>;
|
|
191
|
+
/**
|
|
192
|
+
* Whether a value has the exact shape of a relay bearer: `clusterRelayToken_<64 hex>_<64 hex>`.
|
|
193
|
+
*
|
|
194
|
+
* Shape only: whether it authenticates is Cloudly's to decide against the hash it keeps.
|
|
195
|
+
*/
|
|
196
|
+
export declare const isClusterRelayAuthorization: (valueArg: unknown) => valueArg is string;
|
|
197
|
+
export declare const snapshotClusterRelayAuthorizationExportRequest: (valueArg: unknown) => IClusterRelayAuthorizationExportRequest;
|
|
198
|
+
export declare const snapshotClusterRelayAuthorizationExportReceipt: (valueArg: unknown) => IClusterRelayAuthorizationExportReceipt;
|
|
199
|
+
/**
|
|
200
|
+
* Structural copy of an export answer. A refusal never quotes the answer, because it carries a
|
|
201
|
+
* bearer: every failure is the contract's one value-free message.
|
|
202
|
+
*/
|
|
203
|
+
export declare const snapshotClusterRelayAuthorizationExport: (valueArg: unknown) => IClusterRelayAuthorizationExport;
|
|
204
|
+
/**
|
|
205
|
+
* Joins an export answer to the request it answers, before a reader writes a byte of the bearer.
|
|
206
|
+
*
|
|
207
|
+
* The credential is sealed with its own digest and is the requested cluster's at exactly the
|
|
208
|
+
* requested generation; the bearer names that credential; the receipt names that cluster and that
|
|
209
|
+
* exact credential. Content join only: whether the bearer authenticates is Cloudly's to prove,
|
|
210
|
+
* and Cloudly proves it before it answers.
|
|
211
|
+
*/
|
|
212
|
+
export declare const bindClusterRelayAuthorizationExport: (exportArg: unknown, requestArg: unknown) => Promise<IClusterRelayAuthorizationExport>;
|
|
141
213
|
export {};
|
|
@@ -22,6 +22,20 @@ export const clusterRelayCredentialRefusals = Object.freeze([
|
|
|
22
22
|
/** An expected generation was not the exact current one, or a mutation ID met a changed body. */
|
|
23
23
|
'conflict',
|
|
24
24
|
]);
|
|
25
|
+
/**
|
|
26
|
+
* Every reason Cloudly answers instead of a relay bearer. Its own family, because an export
|
|
27
|
+
* changes no record: it has no `conflict`, and a generation it was not asked for is a mismatch.
|
|
28
|
+
*/
|
|
29
|
+
export const clusterRelayAuthorizationExportRefusals = Object.freeze([
|
|
30
|
+
/** No relay credential is minted for the cluster. */
|
|
31
|
+
'relay-credential-absent',
|
|
32
|
+
/** The credential is at another generation than the one named, before the read or after it. */
|
|
33
|
+
'relay-credential-generation-mismatch',
|
|
34
|
+
/** The version the credential delivered is not stored as an active version. */
|
|
35
|
+
'relay-authorization-missing',
|
|
36
|
+
/** The secret's active version, or its bytes, are not what the credential issued. */
|
|
37
|
+
'relay-authorization-ambiguous',
|
|
38
|
+
]);
|
|
25
39
|
const fail = () => { throw new Error('Cluster relay credential contract rejected.'); };
|
|
26
40
|
const safe = (operationArg) => {
|
|
27
41
|
try {
|
|
@@ -189,4 +203,71 @@ export const bindClusterLegacyRelayCredentialInvalidation = async (invalidationA
|
|
|
189
203
|
return fail();
|
|
190
204
|
}
|
|
191
205
|
};
|
|
192
|
-
|
|
206
|
+
const clusterRelayAuthorizationPrefix = 'clusterRelayToken_';
|
|
207
|
+
const clusterRelayAuthorizationPattern = /^clusterRelayToken_[a-f0-9]{64}_[a-f0-9]{64}$/;
|
|
208
|
+
/**
|
|
209
|
+
* Whether a value has the exact shape of a relay bearer: `clusterRelayToken_<64 hex>_<64 hex>`.
|
|
210
|
+
*
|
|
211
|
+
* Shape only: whether it authenticates is Cloudly's to decide against the hash it keeps.
|
|
212
|
+
*/
|
|
213
|
+
export const isClusterRelayAuthorization = (valueArg) => typeof valueArg === 'string' && clusterRelayAuthorizationPattern.test(valueArg);
|
|
214
|
+
/** The credential identity a relay bearer names: the 64 hex characters after its prefix. */
|
|
215
|
+
const clusterRelayAuthorizationCredentialId = (authorizationArg) => authorizationArg.slice(clusterRelayAuthorizationPrefix.length, clusterRelayAuthorizationPrefix.length + 64);
|
|
216
|
+
export const snapshotClusterRelayAuthorizationExportRequest = (valueArg) => safe(() => {
|
|
217
|
+
const value = networkRecord(captureNetwork(valueArg), ['clusterId', 'expectedGeneration']);
|
|
218
|
+
return {
|
|
219
|
+
clusterId: networkId(value.clusterId),
|
|
220
|
+
expectedGeneration: networkCounter(value.expectedGeneration),
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
export const snapshotClusterRelayAuthorizationExportReceipt = (valueArg) => safe(() => {
|
|
224
|
+
const value = networkRecord(captureNetwork(valueArg), [
|
|
225
|
+
'id', 'clusterId', 'credential', 'actorId', 'exportedAt',
|
|
226
|
+
]);
|
|
227
|
+
return {
|
|
228
|
+
id: networkId(value.id),
|
|
229
|
+
clusterId: networkId(value.clusterId),
|
|
230
|
+
credential: networkReference(value.credential),
|
|
231
|
+
actorId: networkId(value.actorId),
|
|
232
|
+
exportedAt: networkCounter(value.exportedAt),
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
/**
|
|
236
|
+
* Structural copy of an export answer. A refusal never quotes the answer, because it carries a
|
|
237
|
+
* bearer: every failure is the contract's one value-free message.
|
|
238
|
+
*/
|
|
239
|
+
export const snapshotClusterRelayAuthorizationExport = (valueArg) => safe(() => {
|
|
240
|
+
const value = networkRecord(captureNetwork(valueArg), ['credential', 'authorization', 'receipt']);
|
|
241
|
+
requireNetwork(isClusterRelayAuthorization(value.authorization));
|
|
242
|
+
return {
|
|
243
|
+
credential: snapshotClusterRelayCredential(value.credential),
|
|
244
|
+
authorization: value.authorization,
|
|
245
|
+
receipt: snapshotClusterRelayAuthorizationExportReceipt(value.receipt),
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
/**
|
|
249
|
+
* Joins an export answer to the request it answers, before a reader writes a byte of the bearer.
|
|
250
|
+
*
|
|
251
|
+
* The credential is sealed with its own digest and is the requested cluster's at exactly the
|
|
252
|
+
* requested generation; the bearer names that credential; the receipt names that cluster and that
|
|
253
|
+
* exact credential. Content join only: whether the bearer authenticates is Cloudly's to prove,
|
|
254
|
+
* and Cloudly proves it before it answers.
|
|
255
|
+
*/
|
|
256
|
+
export const bindClusterRelayAuthorizationExport = async (exportArg, requestArg) => {
|
|
257
|
+
try {
|
|
258
|
+
const request = snapshotClusterRelayAuthorizationExportRequest(requestArg);
|
|
259
|
+
const answer = snapshotClusterRelayAuthorizationExport(exportArg);
|
|
260
|
+
if ((await validateClusterRelayCredential(answer.credential)).length)
|
|
261
|
+
fail();
|
|
262
|
+
requireNetwork(answer.credential.clusterId === request.clusterId
|
|
263
|
+
&& answer.credential.generation === request.expectedGeneration
|
|
264
|
+
&& clusterRelayAuthorizationCredentialId(answer.authorization) === answer.credential.id
|
|
265
|
+
&& answer.receipt.clusterId === request.clusterId
|
|
266
|
+
&& networkEqual(answer.receipt.credential, networkReferenceOf(answer.credential)));
|
|
267
|
+
return answer;
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
return fail();
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2x1c3RlcnJlbGF5Y3JlZGVudGlhbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2RhdGEvY2x1c3RlcnJlbGF5Y3JlZGVudGlhbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQ0wsd0NBQXdDLEVBQ3hDLHdDQUF3QyxHQUN6QyxNQUFNLGNBQWMsQ0FBQztBQUt0QixPQUFPLEVBQ0wsY0FBYyxFQUNkLGNBQWMsRUFDZCxhQUFhLEVBQ2IsWUFBWSxFQUNaLFdBQVcsRUFDWCxTQUFTLEVBQ1QsYUFBYSxFQUNiLGdCQUFnQixFQUNoQixrQkFBa0IsRUFDbEIsY0FBYyxHQUNmLE1BQU0sK0JBQStCLENBQUM7QUE2SnZDLE1BQU0sQ0FBQyxNQUFNLDhCQUE4QixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDMUQsc0JBQXNCLEVBQUUscUNBQXFDO0lBQzdELDRCQUE0QixFQUFFLDRDQUE0QztJQUMxRSw4QkFBOEIsRUFBRSx5REFBeUQ7Q0FDakYsQ0FBQyxDQUFDO0FBRVosOEZBQThGO0FBQzlGLE1BQU0sQ0FBQyxNQUFNLDhCQUE4QixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDMUQsbUVBQW1FO0lBQ25FLHlCQUF5QjtJQUN6QiwyRUFBMkU7SUFDM0UsMEJBQTBCO0lBQzFCLGlGQUFpRjtJQUNqRixtQ0FBbUM7SUFDbkMsNkZBQTZGO0lBQzdGLDJDQUEyQztJQUMzQyxpR0FBaUc7SUFDakcsNENBQTRDO0lBQzVDLHNGQUFzRjtJQUN0RixrQ0FBa0M7SUFDbEMsaUdBQWlHO0lBQ2pHLFVBQVU7Q0FDRixDQUFDLENBQUM7QUFJWjs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSx1Q0FBdUMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ25FLHFEQUFxRDtJQUNyRCx5QkFBeUI7SUFDekIsK0ZBQStGO0lBQy9GLHNDQUFzQztJQUN0QywrRUFBK0U7SUFDL0UsNkJBQTZCO0lBQzdCLHFGQUFxRjtJQUNy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
|
|
@@ -170,6 +170,40 @@ export interface IReq_Admin_Cloudly_RetireLegacyClusterRelayCredential extends p
|
|
|
170
170
|
};
|
|
171
171
|
response: relayCredentialInterfaces.IClusterLegacyRelayCredentialRetirementResult;
|
|
172
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Answers one cluster's relay bearer to an administrator, so it reaches the relay without an exec
|
|
175
|
+
* into Cloudly's process.
|
|
176
|
+
*
|
|
177
|
+
* Cloudly mints nothing here: a bearer is issued by `mutateClusterRelayCredential` and delivered as
|
|
178
|
+
* the encrypted version the credential names, and this reads exactly that version at exactly
|
|
179
|
+
* `expectedGeneration`, proved against the hash that generation issued. A new bearer is a
|
|
180
|
+
* rotation followed by an export at the new generation. Only the verified credential of a freshly
|
|
181
|
+
* authenticated administrator is read, as for a credential mutation, and Cloudly writes the
|
|
182
|
+
* `receipt` before it answers, so no bearer leaves without an audit record. The bearer is in this
|
|
183
|
+
* answer and nowhere else: never in a log, a receipt or another request. Cloudly refuses with one
|
|
184
|
+
* of `data.clusterRelayAuthorizationExportRefusals`.
|
|
185
|
+
*/
|
|
186
|
+
export interface IReq_Admin_Cloudly_ExportClusterRelayAuthorization extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_Cloudly_ExportClusterRelayAuthorization> {
|
|
187
|
+
method: 'exportClusterRelayAuthorization';
|
|
188
|
+
request: relayCredentialInterfaces.IClusterRelayAuthorizationExportRequest & {
|
|
189
|
+
identity: userInterfaces.IIdentityCredential;
|
|
190
|
+
};
|
|
191
|
+
response: relayCredentialInterfaces.IClusterRelayAuthorizationExport;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The audit trail of one cluster's relay bearer exports: every receipt, oldest first. Value-free,
|
|
195
|
+
* like the receipts themselves; empty while no bearer was ever exported.
|
|
196
|
+
*/
|
|
197
|
+
export interface IReq_Admin_Cloudly_GetClusterRelayAuthorizationExports extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_Cloudly_GetClusterRelayAuthorizationExports> {
|
|
198
|
+
method: 'getClusterRelayAuthorizationExports';
|
|
199
|
+
request: {
|
|
200
|
+
identity: userInterfaces.IIdentityCredential;
|
|
201
|
+
clusterId: string;
|
|
202
|
+
};
|
|
203
|
+
response: {
|
|
204
|
+
receipts: relayCredentialInterfaces.IClusterRelayAuthorizationExportReceipt[];
|
|
205
|
+
};
|
|
206
|
+
}
|
|
173
207
|
/**
|
|
174
208
|
* Cloudly hands the cluster relay the DNS lease renewals it has signed ahead of time for one node.
|
|
175
209
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/interfaces",
|
|
3
|
-
"version": "32.
|
|
3
|
+
"version": "32.41.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
|
|
6
6
|
"exports": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@api.global/typedrequest-interfaces": "^7.1.0",
|
|
25
|
-
"@push.rocks/smartcrypto": "^
|
|
25
|
+
"@push.rocks/smartcrypto": "^3.0.0",
|
|
26
26
|
"@push.rocks/smartlog": "^4.0.0",
|
|
27
27
|
"@tsclass/tsclass": "^9.5.1"
|
|
28
28
|
},
|
package/readme.md
CHANGED
|
@@ -2723,8 +2723,9 @@ cluster authority every cluster-side component used before it. `IClusterRelayCre
|
|
|
2723
2723
|
metadata: the cluster, a `generation` that advances by one per accepted mint or rotation, the
|
|
2724
2724
|
machine `userId` it authenticates as, and `delivery`, the `ISecretVersionReference` of the
|
|
2725
2725
|
immutable encrypted version the bearer was delivered as. No record, receipt, request or response
|
|
2726
|
-
of this contract carries a bearer, a hash or key material,
|
|
2727
|
-
|
|
2726
|
+
of this contract carries a bearer, a hash or key material, except the one answer that exists to
|
|
2727
|
+
hand the bearer over (see the export below), and every reader is exact-keyed, so a peer that adds
|
|
2728
|
+
a secret-looking member is refused rather than copied.
|
|
2728
2729
|
|
|
2729
2730
|
The credential authenticates the cluster's whole control-plane session, exactly what the shared
|
|
2730
2731
|
authority authenticated, so a relay changes one credential and keeps every call it makes. The gain
|
|
@@ -2776,7 +2777,36 @@ it names or that record's revoked successor — and to a registration whose `cre
|
|
|
2776
2777
|
invalidation's `replacement`. It is a content check; dereferencing each record as current and
|
|
2777
2778
|
requiring the live socket remain Cloudly's.
|
|
2778
2779
|
|
|
2779
|
-
|
|
2780
|
+
`exportClusterRelayAuthorization { identity, clusterId, expectedGeneration }` answers the bearer
|
|
2781
|
+
itself, so an operator hands it to the relay without an exec into Cloudly's process. Cloudly mints
|
|
2782
|
+
nothing to answer it: the bearer is the encrypted version the credential's `delivery` names, read
|
|
2783
|
+
at exactly `expectedGeneration` (never a minimum) and proved against the hash that generation
|
|
2784
|
+
issued, so a rotation committing meanwhile is refused rather than answered. A new bearer is a
|
|
2785
|
+
`rotate` followed by an export at the new generation. It requires a freshly authenticated
|
|
2786
|
+
administrator, exactly as a credential mutation does. `IClusterRelayAuthorizationExport` carries
|
|
2787
|
+
the sealed `credential` at that generation, the `authorization` (the bearer, byte for byte what the
|
|
2788
|
+
relay presents: `clusterRelayToken_<credential id>_<64 hex>`, no line terminator) and the
|
|
2789
|
+
`receipt`. It is the only answer of this contract that carries a bearer; Cloudly never logs it, and
|
|
2790
|
+
its reader writes it where the relay reads it and nowhere else.
|
|
2791
|
+
|
|
2792
|
+
Every answered export is audited: Cloudly writes `IClusterRelayAuthorizationExportReceipt`
|
|
2793
|
+
(`id`, `clusterId`, the exact `credential` reference, the administrator's `actorId`,
|
|
2794
|
+
`exportedAt`) before it answers, so no bearer leaves without a record, and a retried export is a
|
|
2795
|
+
second receipt rather than a replay. A receipt is value-free: no bearer, no hash.
|
|
2796
|
+
`getClusterRelayAuthorizationExports { identity, clusterId }` answers a cluster's receipts, oldest
|
|
2797
|
+
first. Refusals are `data.clusterRelayAuthorizationExportRefusals`
|
|
2798
|
+
(`data.TClusterRelayAuthorizationExportRefusal`): `relay-credential-absent`,
|
|
2799
|
+
`relay-credential-generation-mismatch`, `relay-authorization-missing` and
|
|
2800
|
+
`relay-authorization-ambiguous`. A reader checks an answer with
|
|
2801
|
+
`bindClusterRelayAuthorizationExport(answer, { clusterId, expectedGeneration })` before it writes a
|
|
2802
|
+
byte: the credential's digest holds, it is the requested cluster's at the requested generation, the
|
|
2803
|
+
bearer names that credential, and the receipt names that exact credential. Every refusal is the
|
|
2804
|
+
contract's one value-free message and never quotes the answer. `isClusterRelayAuthorization`
|
|
2805
|
+
states the bearer's shape, and `snapshotClusterRelayAuthorizationExportRequest`,
|
|
2806
|
+
`snapshotClusterRelayAuthorizationExportReceipt` and `snapshotClusterRelayAuthorizationExport` are
|
|
2807
|
+
the exact-keyed readers.
|
|
2808
|
+
|
|
2809
|
+
All five requests read only the verified `IIdentityCredential`, never an identity member a caller
|
|
2780
2810
|
states about itself.
|
|
2781
2811
|
|
|
2782
2812
|
### Spark Legacy Runtime Retirement
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -128,6 +128,53 @@ export interface IClusterLegacyRelayCredentialRetirementResult {
|
|
|
128
128
|
replayed: boolean;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* What an administrator names to export one cluster's relay bearer: the cluster, at exactly the
|
|
133
|
+
* generation `getClusterRelayCredential` answered. Never a minimum: a rotation that committed
|
|
134
|
+
* after that read is refused rather than answered with a bearer the operator did not ask for.
|
|
135
|
+
*/
|
|
136
|
+
export interface IClusterRelayAuthorizationExportRequest {
|
|
137
|
+
clusterId: string;
|
|
138
|
+
expectedGeneration: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The audit record of one answered export, which Cloudly writes before it answers the bearer.
|
|
143
|
+
*
|
|
144
|
+
* Append-only and value-free: it names who received which credential's bearer and when, never
|
|
145
|
+
* the bearer or a hash of it. Every answered export writes one, so a retried export is a second
|
|
146
|
+
* receipt rather than a replay: each is a separate handover of the same bearer.
|
|
147
|
+
*/
|
|
148
|
+
export interface IClusterRelayAuthorizationExportReceipt {
|
|
149
|
+
id: string;
|
|
150
|
+
clusterId: string;
|
|
151
|
+
/** The exact relay credential whose bearer was answered: identity, generation and digest. */
|
|
152
|
+
credential: IRuntimeAuthorityReference;
|
|
153
|
+
/** The administrator the bearer was answered to, read from the verified credential. */
|
|
154
|
+
actorId: string;
|
|
155
|
+
exportedAt: number;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* One cluster's relay bearer, answered to an administrator so it can be handed to the relay
|
|
160
|
+
* without reaching into Cloudly's process.
|
|
161
|
+
*
|
|
162
|
+
* The only member of this contract that carries a bearer. It is the delivered version the
|
|
163
|
+
* credential names, at the requested generation, proved against the hash that generation issued;
|
|
164
|
+
* Cloudly mints nothing to answer it. The reader owns the bytes: it writes them where the relay
|
|
165
|
+
* reads them and never logs, prints or persists them anywhere else.
|
|
166
|
+
*/
|
|
167
|
+
export interface IClusterRelayAuthorizationExport {
|
|
168
|
+
/** The relay credential the bearer authenticates as, at exactly the requested generation. */
|
|
169
|
+
credential: IClusterRelayCredential;
|
|
170
|
+
/**
|
|
171
|
+
* The bearer, byte for byte what the relay presents: `clusterRelayToken_`, the credential's
|
|
172
|
+
* 64-hex `id`, `_` and 64 hex characters, with no line terminator.
|
|
173
|
+
*/
|
|
174
|
+
authorization: string;
|
|
175
|
+
receipt: IClusterRelayAuthorizationExportReceipt;
|
|
176
|
+
}
|
|
177
|
+
|
|
131
178
|
export const clusterRelayCredentialContract = Object.freeze({
|
|
132
179
|
credentialDigestDomain: 'serve.zone/cluster-relay-credential',
|
|
133
180
|
legacyCredentialDigestDomain: 'serve.zone/cluster-relay-legacy-credential',
|
|
@@ -154,6 +201,24 @@ export const clusterRelayCredentialRefusals = Object.freeze([
|
|
|
154
201
|
|
|
155
202
|
export type TClusterRelayCredentialRefusal = typeof clusterRelayCredentialRefusals[number];
|
|
156
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Every reason Cloudly answers instead of a relay bearer. Its own family, because an export
|
|
206
|
+
* changes no record: it has no `conflict`, and a generation it was not asked for is a mismatch.
|
|
207
|
+
*/
|
|
208
|
+
export const clusterRelayAuthorizationExportRefusals = Object.freeze([
|
|
209
|
+
/** No relay credential is minted for the cluster. */
|
|
210
|
+
'relay-credential-absent',
|
|
211
|
+
/** The credential is at another generation than the one named, before the read or after it. */
|
|
212
|
+
'relay-credential-generation-mismatch',
|
|
213
|
+
/** The version the credential delivered is not stored as an active version. */
|
|
214
|
+
'relay-authorization-missing',
|
|
215
|
+
/** The secret's active version, or its bytes, are not what the credential issued. */
|
|
216
|
+
'relay-authorization-ambiguous',
|
|
217
|
+
] as const);
|
|
218
|
+
|
|
219
|
+
export type TClusterRelayAuthorizationExportRefusal =
|
|
220
|
+
typeof clusterRelayAuthorizationExportRefusals[number];
|
|
221
|
+
|
|
157
222
|
const fail = (): never => { throw new Error('Cluster relay credential contract rejected.'); };
|
|
158
223
|
const safe = <T>(operationArg: () => T): T => {
|
|
159
224
|
try { return operationArg(); } catch { return fail(); }
|
|
@@ -359,3 +424,87 @@ export const bindClusterLegacyRelayCredentialInvalidation = async (
|
|
|
359
424
|
return { invalidation, legacyCredential, acceptedRegistration };
|
|
360
425
|
} catch { return fail(); }
|
|
361
426
|
};
|
|
427
|
+
|
|
428
|
+
const clusterRelayAuthorizationPrefix = 'clusterRelayToken_';
|
|
429
|
+
const clusterRelayAuthorizationPattern = /^clusterRelayToken_[a-f0-9]{64}_[a-f0-9]{64}$/;
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Whether a value has the exact shape of a relay bearer: `clusterRelayToken_<64 hex>_<64 hex>`.
|
|
433
|
+
*
|
|
434
|
+
* Shape only: whether it authenticates is Cloudly's to decide against the hash it keeps.
|
|
435
|
+
*/
|
|
436
|
+
export const isClusterRelayAuthorization = (valueArg: unknown): valueArg is string =>
|
|
437
|
+
typeof valueArg === 'string' && clusterRelayAuthorizationPattern.test(valueArg);
|
|
438
|
+
|
|
439
|
+
/** The credential identity a relay bearer names: the 64 hex characters after its prefix. */
|
|
440
|
+
const clusterRelayAuthorizationCredentialId = (authorizationArg: string): string =>
|
|
441
|
+
authorizationArg.slice(
|
|
442
|
+
clusterRelayAuthorizationPrefix.length,
|
|
443
|
+
clusterRelayAuthorizationPrefix.length + 64,
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
export const snapshotClusterRelayAuthorizationExportRequest = (
|
|
447
|
+
valueArg: unknown,
|
|
448
|
+
): IClusterRelayAuthorizationExportRequest => safe(() => {
|
|
449
|
+
const value = networkRecord(captureNetwork(valueArg), ['clusterId', 'expectedGeneration']);
|
|
450
|
+
return {
|
|
451
|
+
clusterId: networkId(value.clusterId),
|
|
452
|
+
expectedGeneration: networkCounter(value.expectedGeneration),
|
|
453
|
+
};
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
export const snapshotClusterRelayAuthorizationExportReceipt = (
|
|
457
|
+
valueArg: unknown,
|
|
458
|
+
): IClusterRelayAuthorizationExportReceipt => safe(() => {
|
|
459
|
+
const value = networkRecord(captureNetwork(valueArg), [
|
|
460
|
+
'id', 'clusterId', 'credential', 'actorId', 'exportedAt',
|
|
461
|
+
]);
|
|
462
|
+
return {
|
|
463
|
+
id: networkId(value.id),
|
|
464
|
+
clusterId: networkId(value.clusterId),
|
|
465
|
+
credential: networkReference(value.credential),
|
|
466
|
+
actorId: networkId(value.actorId),
|
|
467
|
+
exportedAt: networkCounter(value.exportedAt),
|
|
468
|
+
};
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Structural copy of an export answer. A refusal never quotes the answer, because it carries a
|
|
473
|
+
* bearer: every failure is the contract's one value-free message.
|
|
474
|
+
*/
|
|
475
|
+
export const snapshotClusterRelayAuthorizationExport = (
|
|
476
|
+
valueArg: unknown,
|
|
477
|
+
): IClusterRelayAuthorizationExport => safe(() => {
|
|
478
|
+
const value = networkRecord(captureNetwork(valueArg), ['credential', 'authorization', 'receipt']);
|
|
479
|
+
requireNetwork(isClusterRelayAuthorization(value.authorization));
|
|
480
|
+
return {
|
|
481
|
+
credential: snapshotClusterRelayCredential(value.credential),
|
|
482
|
+
authorization: value.authorization,
|
|
483
|
+
receipt: snapshotClusterRelayAuthorizationExportReceipt(value.receipt),
|
|
484
|
+
};
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Joins an export answer to the request it answers, before a reader writes a byte of the bearer.
|
|
489
|
+
*
|
|
490
|
+
* The credential is sealed with its own digest and is the requested cluster's at exactly the
|
|
491
|
+
* requested generation; the bearer names that credential; the receipt names that cluster and that
|
|
492
|
+
* exact credential. Content join only: whether the bearer authenticates is Cloudly's to prove,
|
|
493
|
+
* and Cloudly proves it before it answers.
|
|
494
|
+
*/
|
|
495
|
+
export const bindClusterRelayAuthorizationExport = async (
|
|
496
|
+
exportArg: unknown,
|
|
497
|
+
requestArg: unknown,
|
|
498
|
+
): Promise<IClusterRelayAuthorizationExport> => {
|
|
499
|
+
try {
|
|
500
|
+
const request = snapshotClusterRelayAuthorizationExportRequest(requestArg);
|
|
501
|
+
const answer = snapshotClusterRelayAuthorizationExport(exportArg);
|
|
502
|
+
if ((await validateClusterRelayCredential(answer.credential)).length) fail();
|
|
503
|
+
requireNetwork(answer.credential.clusterId === request.clusterId
|
|
504
|
+
&& answer.credential.generation === request.expectedGeneration
|
|
505
|
+
&& clusterRelayAuthorizationCredentialId(answer.authorization) === answer.credential.id
|
|
506
|
+
&& answer.receipt.clusterId === request.clusterId
|
|
507
|
+
&& networkEqual(answer.receipt.credential, networkReferenceOf(answer.credential)));
|
|
508
|
+
return answer;
|
|
509
|
+
} catch { return fail(); }
|
|
510
|
+
};
|
package/ts/requests/cluster.ts
CHANGED
|
@@ -219,6 +219,50 @@ extends plugins.typedrequestInterfaces.implementsTR<
|
|
|
219
219
|
response: relayCredentialInterfaces.IClusterLegacyRelayCredentialRetirementResult;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Answers one cluster's relay bearer to an administrator, so it reaches the relay without an exec
|
|
224
|
+
* into Cloudly's process.
|
|
225
|
+
*
|
|
226
|
+
* Cloudly mints nothing here: a bearer is issued by `mutateClusterRelayCredential` and delivered as
|
|
227
|
+
* the encrypted version the credential names, and this reads exactly that version at exactly
|
|
228
|
+
* `expectedGeneration`, proved against the hash that generation issued. A new bearer is a
|
|
229
|
+
* rotation followed by an export at the new generation. Only the verified credential of a freshly
|
|
230
|
+
* authenticated administrator is read, as for a credential mutation, and Cloudly writes the
|
|
231
|
+
* `receipt` before it answers, so no bearer leaves without an audit record. The bearer is in this
|
|
232
|
+
* answer and nowhere else: never in a log, a receipt or another request. Cloudly refuses with one
|
|
233
|
+
* of `data.clusterRelayAuthorizationExportRefusals`.
|
|
234
|
+
*/
|
|
235
|
+
export interface IReq_Admin_Cloudly_ExportClusterRelayAuthorization
|
|
236
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
237
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
238
|
+
IReq_Admin_Cloudly_ExportClusterRelayAuthorization
|
|
239
|
+
> {
|
|
240
|
+
method: 'exportClusterRelayAuthorization';
|
|
241
|
+
request: relayCredentialInterfaces.IClusterRelayAuthorizationExportRequest & {
|
|
242
|
+
identity: userInterfaces.IIdentityCredential;
|
|
243
|
+
};
|
|
244
|
+
response: relayCredentialInterfaces.IClusterRelayAuthorizationExport;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The audit trail of one cluster's relay bearer exports: every receipt, oldest first. Value-free,
|
|
249
|
+
* like the receipts themselves; empty while no bearer was ever exported.
|
|
250
|
+
*/
|
|
251
|
+
export interface IReq_Admin_Cloudly_GetClusterRelayAuthorizationExports
|
|
252
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
253
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
254
|
+
IReq_Admin_Cloudly_GetClusterRelayAuthorizationExports
|
|
255
|
+
> {
|
|
256
|
+
method: 'getClusterRelayAuthorizationExports';
|
|
257
|
+
request: {
|
|
258
|
+
identity: userInterfaces.IIdentityCredential;
|
|
259
|
+
clusterId: string;
|
|
260
|
+
};
|
|
261
|
+
response: {
|
|
262
|
+
receipts: relayCredentialInterfaces.IClusterRelayAuthorizationExportReceipt[];
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
222
266
|
/**
|
|
223
267
|
* Cloudly hands the cluster relay the DNS lease renewals it has signed ahead of time for one node.
|
|
224
268
|
*
|