@serve.zone/interfaces 32.60.0 → 32.62.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 +4 -2
- package/changelog.md +32 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/coremail.d.ts +37 -1
- package/dist_ts/data/coremail.js +23 -1
- package/dist_ts/data/coremail.runtime.d.ts +10 -1
- package/dist_ts/data/coremail.runtime.js +23 -1
- package/dist_ts/data/delegatedadmintoken.d.ts +142 -0
- package/dist_ts/data/delegatedadmintoken.js +197 -0
- package/dist_ts/data/gateway.d.ts +23 -4
- package/dist_ts/data/gateway.js +1 -1
- package/dist_ts/data/index.d.ts +2 -0
- package/dist_ts/data/index.js +3 -1
- package/dist_ts/data/mail.d.ts +36 -0
- package/dist_ts/data/mail.js +45 -2
- package/dist_ts/data/runtimeassignment.d.ts +4 -1
- package/dist_ts/data/runtimeassignment.js +5 -2
- package/dist_ts/data/secret.d.ts +23 -0
- package/dist_ts/data/secret.js +33 -1
- package/dist_ts/data/serviceruntime.d.ts +14 -1
- package/dist_ts/data/serviceruntime.js +2 -2
- package/dist_ts/data/settings.d.ts +21 -0
- package/dist_ts/data/settings.js +38 -1
- package/dist_ts/data/workloadreplicaidentity.d.ts +101 -0
- package/dist_ts/data/workloadreplicaidentity.js +159 -0
- package/dist_ts/requests/admin.d.ts +52 -0
- package/dist_ts/requests/coremail.d.ts +6 -1
- package/dist_ts/requests/identity.d.ts +4 -0
- package/package.json +5 -8
- package/readme.md +154 -8
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/coremail.runtime.ts +26 -1
- package/ts/data/coremail.ts +42 -1
- package/ts/data/delegatedadmintoken.ts +282 -0
- package/ts/data/gateway.ts +24 -3
- package/ts/data/index.ts +2 -0
- package/ts/data/mail.ts +48 -0
- package/ts/data/runtimeassignment.ts +4 -1
- package/ts/data/secret.ts +38 -0
- package/ts/data/serviceruntime.ts +14 -1
- package/ts/data/settings.ts +46 -0
- package/ts/data/workloadreplicaidentity.ts +219 -0
- package/ts/requests/admin.ts +51 -0
- package/ts/requests/coremail.ts +6 -1
- package/ts/requests/identity.ts +4 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { TSha256Digest } from './immutableimage.js';
|
|
2
|
+
import type { IRuntimeAssignment } from './runtimeassignment.js';
|
|
3
|
+
import type { TServiceRuntimeRefusal } from './serviceruntime.js';
|
|
4
|
+
/**
|
|
5
|
+
* Which replica of which workload a process is, as its runtime states it.
|
|
6
|
+
*
|
|
7
|
+
* Every member is the runtime assignment's own (`buildWorkloadReplicaIdentity`), so a workload, its
|
|
8
|
+
* runtime and its controller name a replica with the same four values. `attemptId` is the one
|
|
9
|
+
* execution attempt: a rollout, a replacement or a restart onto another attempt is a new attempt,
|
|
10
|
+
* so the attempt is what a workload reports to correlate itself with the controller's roster.
|
|
11
|
+
*/
|
|
12
|
+
export interface IWorkloadReplicaIdentity {
|
|
13
|
+
/** The service the replica runs (`IRuntimeAssignment.serviceId`). */
|
|
14
|
+
serviceId: string;
|
|
15
|
+
/** The stable logical replica (`IRuntimeAssignment.replicaId`), a `isRuntimeReplicaId` name. */
|
|
16
|
+
replicaId: string;
|
|
17
|
+
/** The one execution attempt (`IRuntimeAssignment.attemptId`). */
|
|
18
|
+
attemptId: string;
|
|
19
|
+
/** The root descriptor digest the replica runs (`IRuntimeAssignment.workload.imageDigest`). */
|
|
20
|
+
imageDigest: TSha256Digest;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The environment variable names every runtime injects into every workload it runs, one per
|
|
24
|
+
* identity member. The one spelling Pallet, Onebox, Cloudly's composition refusal and every
|
|
25
|
+
* workload that reads its identity share.
|
|
26
|
+
*/
|
|
27
|
+
export declare const workloadReplicaIdentityEnvironmentNames: Readonly<{
|
|
28
|
+
readonly serviceId: 'SERVEZONE_WORKLOAD_SERVICE_ID';
|
|
29
|
+
readonly replicaId: 'SERVEZONE_WORKLOAD_REPLICA_ID';
|
|
30
|
+
readonly attemptId: 'SERVEZONE_WORKLOAD_ATTEMPT_ID';
|
|
31
|
+
readonly imageDigest: 'SERVEZONE_WORKLOAD_IMAGE_DIGEST';
|
|
32
|
+
}>;
|
|
33
|
+
export type TWorkloadReplicaIdentityEnvironmentName = typeof workloadReplicaIdentityEnvironmentNames[keyof IWorkloadReplicaIdentity];
|
|
34
|
+
/**
|
|
35
|
+
* The bounds the injection adds to a runtime config's own.
|
|
36
|
+
*
|
|
37
|
+
* `IRuntimeConfig.environment` keeps its own bound (`runtimeConfigContract.maximumEnvironmentEntries`),
|
|
38
|
+
* so a service states at most that many entries. A runtime's executor admits the config's entries
|
|
39
|
+
* plus the injected identity, `maximumExecutorEnvironmentEntries`, computed from both, never a
|
|
40
|
+
* literal of its own.
|
|
41
|
+
*/
|
|
42
|
+
export declare const workloadReplicaIdentityContract: Readonly<{
|
|
43
|
+
injectedEnvironmentEntries: number;
|
|
44
|
+
maximumExecutorEnvironmentEntries: number;
|
|
45
|
+
}>;
|
|
46
|
+
/** Every reason a value is not exactly a workload replica identity; empty when it is one. */
|
|
47
|
+
export declare const validateWorkloadReplicaIdentity: (valueArg: unknown) => string[];
|
|
48
|
+
/** A detached, frozen copy of exactly a workload replica identity; throws naming every violation. */
|
|
49
|
+
export declare const snapshotWorkloadReplicaIdentity: (valueArg: unknown) => IWorkloadReplicaIdentity;
|
|
50
|
+
/**
|
|
51
|
+
* The identity of the replica one runtime assignment runs.
|
|
52
|
+
*
|
|
53
|
+
* Read from the assignment the runtime admitted, through its exact schema, so the answer always
|
|
54
|
+
* satisfies `validateWorkloadReplicaIdentity`. It proves nothing about the assignment's digest or
|
|
55
|
+
* admission; the runtime has judged those before it runs anything.
|
|
56
|
+
*/
|
|
57
|
+
export declare const buildWorkloadReplicaIdentity: (assignmentArg: IRuntimeAssignment) => IWorkloadReplicaIdentity;
|
|
58
|
+
/**
|
|
59
|
+
* The injected names among `namesArg`, sorted and unique: empty when none collides.
|
|
60
|
+
*
|
|
61
|
+
* Cloudly's composition refusal passes the keys of a replica's effective environment together with
|
|
62
|
+
* the variable names of its launcher-environment secrets, because WorkloadInit sets a secret over a
|
|
63
|
+
* plain variable of the same name and so would replace the injected identity just as silently.
|
|
64
|
+
*/
|
|
65
|
+
export declare const findWorkloadReplicaIdentityEnvironmentConflicts: (namesArg: readonly string[]) => TWorkloadReplicaIdentityEnvironmentName[];
|
|
66
|
+
/** The one reason a workload environment cannot carry its replica identity. */
|
|
67
|
+
export type TWorkloadReplicaEnvironmentRefusal = Extract<TServiceRuntimeRefusal, 'workload-identity-environment-conflict'>;
|
|
68
|
+
/** What a replica runs with, or the one reason its identity cannot be injected. */
|
|
69
|
+
export type TWorkloadReplicaEnvironmentComposition = {
|
|
70
|
+
status: 'composed';
|
|
71
|
+
environment: Record<string, string>;
|
|
72
|
+
} | {
|
|
73
|
+
status: 'refused';
|
|
74
|
+
reason: TWorkloadReplicaEnvironmentRefusal;
|
|
75
|
+
message: string;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* The environment one replica runs with: its runtime config's environment with the replica's
|
|
79
|
+
* identity added under `workloadReplicaIdentityEnvironmentNames`.
|
|
80
|
+
*
|
|
81
|
+
* Every runtime injects the identity into every workload, whether the workload reads it or not. A
|
|
82
|
+
* config environment that already states one of the names is refused
|
|
83
|
+
* `workload-identity-environment-conflict`, naming every such key and none of their values, rather
|
|
84
|
+
* than having one value silently replace the other. The reason stays inside the runtime: only
|
|
85
|
+
* Cloudly states it on the wire, in a service's status. A Pallet reports the refused run as the
|
|
86
|
+
* existing `TRuntimeAssignmentFailureCause` `{ kind: 'refused', reason: 'invalid' }`, which every
|
|
87
|
+
* reader since 32.45.0 reads and a controller never re-places with an identical configuration. An
|
|
88
|
+
* identity that is not exactly one is the caller's error and is thrown. The inputs are not
|
|
89
|
+
* changed. A config within
|
|
90
|
+
* `runtimeConfigContract.maximumEnvironmentEntries` composes to at most
|
|
91
|
+
* `workloadReplicaIdentityContract.maximumExecutorEnvironmentEntries` entries.
|
|
92
|
+
*/
|
|
93
|
+
export declare const composeWorkloadReplicaEnvironment: (environmentArg: Readonly<Record<string, string>>, identityArg: IWorkloadReplicaIdentity) => TWorkloadReplicaEnvironmentComposition;
|
|
94
|
+
/**
|
|
95
|
+
* The replica identity a workload reads from its own environment (`process.env`).
|
|
96
|
+
*
|
|
97
|
+
* Reads the four injected names only, as own data properties, and nothing else of the environment.
|
|
98
|
+
* Throws naming every name that is missing and every name whose value is not a valid member, never
|
|
99
|
+
* a value, so a workload refuses to start with the exact variable its runtime did not inject.
|
|
100
|
+
*/
|
|
101
|
+
export declare const readWorkloadReplicaIdentityEnvironment: (environmentArg: Readonly<Record<string, string | undefined>>) => IWorkloadReplicaIdentity;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { isSha256Digest } from './immutableimage.js';
|
|
2
|
+
import { isRuntimeReplicaId, snapshotRuntimeAssignment } from './runtimeassignment.js';
|
|
3
|
+
import { runtimeConfigContract } from './runtimeconfig.js';
|
|
4
|
+
import { hasExactKeys, readDataProperties } from '../private/dataproperties.js';
|
|
5
|
+
/**
|
|
6
|
+
* The environment variable names every runtime injects into every workload it runs, one per
|
|
7
|
+
* identity member. The one spelling Pallet, Onebox, Cloudly's composition refusal and every
|
|
8
|
+
* workload that reads its identity share.
|
|
9
|
+
*/
|
|
10
|
+
export const workloadReplicaIdentityEnvironmentNames = Object.freeze({
|
|
11
|
+
serviceId: 'SERVEZONE_WORKLOAD_SERVICE_ID',
|
|
12
|
+
replicaId: 'SERVEZONE_WORKLOAD_REPLICA_ID',
|
|
13
|
+
attemptId: 'SERVEZONE_WORKLOAD_ATTEMPT_ID',
|
|
14
|
+
imageDigest: 'SERVEZONE_WORKLOAD_IMAGE_DIGEST',
|
|
15
|
+
});
|
|
16
|
+
const identityKeys = Object.freeze(Object.keys(workloadReplicaIdentityEnvironmentNames));
|
|
17
|
+
const identityEnvironmentNames = Object.freeze(Object.values(workloadReplicaIdentityEnvironmentNames));
|
|
18
|
+
/**
|
|
19
|
+
* The bounds the injection adds to a runtime config's own.
|
|
20
|
+
*
|
|
21
|
+
* `IRuntimeConfig.environment` keeps its own bound (`runtimeConfigContract.maximumEnvironmentEntries`),
|
|
22
|
+
* so a service states at most that many entries. A runtime's executor admits the config's entries
|
|
23
|
+
* plus the injected identity, `maximumExecutorEnvironmentEntries`, computed from both, never a
|
|
24
|
+
* literal of its own.
|
|
25
|
+
*/
|
|
26
|
+
export const workloadReplicaIdentityContract = Object.freeze({
|
|
27
|
+
injectedEnvironmentEntries: identityKeys.length,
|
|
28
|
+
maximumExecutorEnvironmentEntries: runtimeConfigContract.maximumEnvironmentEntries + identityKeys.length,
|
|
29
|
+
});
|
|
30
|
+
const isIdentifier = (valueArg) => typeof valueArg === 'string' && /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/.test(valueArg);
|
|
31
|
+
/** What each member must hold, in the runtime assignment's own vocabulary. */
|
|
32
|
+
const memberRules = Object.freeze({
|
|
33
|
+
serviceId: { accepts: isIdentifier, requirement: 'a canonical identifier' },
|
|
34
|
+
replicaId: { accepts: isRuntimeReplicaId, requirement: 'a replica id' },
|
|
35
|
+
attemptId: { accepts: isIdentifier, requirement: 'a canonical identifier' },
|
|
36
|
+
imageDigest: {
|
|
37
|
+
accepts: (valueArg) => typeof valueArg === 'string' && isSha256Digest(valueArg),
|
|
38
|
+
requirement: 'a sha256 digest',
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
const judgeIdentity = (valueArg) => {
|
|
42
|
+
const record = readDataProperties(valueArg);
|
|
43
|
+
if (record === null) {
|
|
44
|
+
return { record, errors: ['workload replica identity must be a plain record of data properties'] };
|
|
45
|
+
}
|
|
46
|
+
const errors = [];
|
|
47
|
+
if (!hasExactKeys(record, identityKeys)) {
|
|
48
|
+
errors.push(`workload replica identity must state exactly ${identityKeys.join(', ')}`);
|
|
49
|
+
}
|
|
50
|
+
for (const key of identityKeys) {
|
|
51
|
+
if (Object.hasOwn(record, key) && !memberRules[key].accepts(record[key])) {
|
|
52
|
+
errors.push(`${key} must be ${memberRules[key].requirement}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { record, errors };
|
|
56
|
+
};
|
|
57
|
+
/** Every reason a value is not exactly a workload replica identity; empty when it is one. */
|
|
58
|
+
export const validateWorkloadReplicaIdentity = (valueArg) => judgeIdentity(valueArg).errors;
|
|
59
|
+
/** A detached, frozen copy of exactly a workload replica identity; throws naming every violation. */
|
|
60
|
+
export const snapshotWorkloadReplicaIdentity = (valueArg) => {
|
|
61
|
+
const { record, errors } = judgeIdentity(valueArg);
|
|
62
|
+
if (record === null || errors.length > 0) {
|
|
63
|
+
throw new Error(`workload replica identity refused: ${errors.join('; ')}`);
|
|
64
|
+
}
|
|
65
|
+
return Object.freeze({
|
|
66
|
+
serviceId: record.serviceId,
|
|
67
|
+
replicaId: record.replicaId,
|
|
68
|
+
attemptId: record.attemptId,
|
|
69
|
+
imageDigest: record.imageDigest,
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* The identity of the replica one runtime assignment runs.
|
|
74
|
+
*
|
|
75
|
+
* Read from the assignment the runtime admitted, through its exact schema, so the answer always
|
|
76
|
+
* satisfies `validateWorkloadReplicaIdentity`. It proves nothing about the assignment's digest or
|
|
77
|
+
* admission; the runtime has judged those before it runs anything.
|
|
78
|
+
*/
|
|
79
|
+
export const buildWorkloadReplicaIdentity = (assignmentArg) => {
|
|
80
|
+
const assignment = snapshotRuntimeAssignment(assignmentArg);
|
|
81
|
+
return Object.freeze({
|
|
82
|
+
serviceId: assignment.serviceId,
|
|
83
|
+
replicaId: assignment.replicaId,
|
|
84
|
+
attemptId: assignment.attemptId,
|
|
85
|
+
imageDigest: assignment.workload.imageDigest,
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* The injected names among `namesArg`, sorted and unique: empty when none collides.
|
|
90
|
+
*
|
|
91
|
+
* Cloudly's composition refusal passes the keys of a replica's effective environment together with
|
|
92
|
+
* the variable names of its launcher-environment secrets, because WorkloadInit sets a secret over a
|
|
93
|
+
* plain variable of the same name and so would replace the injected identity just as silently.
|
|
94
|
+
*/
|
|
95
|
+
export const findWorkloadReplicaIdentityEnvironmentConflicts = (namesArg) => identityEnvironmentNames
|
|
96
|
+
.filter((nameArg) => namesArg.includes(nameArg))
|
|
97
|
+
.sort();
|
|
98
|
+
/**
|
|
99
|
+
* The environment one replica runs with: its runtime config's environment with the replica's
|
|
100
|
+
* identity added under `workloadReplicaIdentityEnvironmentNames`.
|
|
101
|
+
*
|
|
102
|
+
* Every runtime injects the identity into every workload, whether the workload reads it or not. A
|
|
103
|
+
* config environment that already states one of the names is refused
|
|
104
|
+
* `workload-identity-environment-conflict`, naming every such key and none of their values, rather
|
|
105
|
+
* than having one value silently replace the other. The reason stays inside the runtime: only
|
|
106
|
+
* Cloudly states it on the wire, in a service's status. A Pallet reports the refused run as the
|
|
107
|
+
* existing `TRuntimeAssignmentFailureCause` `{ kind: 'refused', reason: 'invalid' }`, which every
|
|
108
|
+
* reader since 32.45.0 reads and a controller never re-places with an identical configuration. An
|
|
109
|
+
* identity that is not exactly one is the caller's error and is thrown. The inputs are not
|
|
110
|
+
* changed. A config within
|
|
111
|
+
* `runtimeConfigContract.maximumEnvironmentEntries` composes to at most
|
|
112
|
+
* `workloadReplicaIdentityContract.maximumExecutorEnvironmentEntries` entries.
|
|
113
|
+
*/
|
|
114
|
+
export const composeWorkloadReplicaEnvironment = (environmentArg, identityArg) => {
|
|
115
|
+
const identity = snapshotWorkloadReplicaIdentity(identityArg);
|
|
116
|
+
const conflicts = findWorkloadReplicaIdentityEnvironmentConflicts(Object.keys(environmentArg));
|
|
117
|
+
if (conflicts.length > 0) {
|
|
118
|
+
return { status: 'refused', reason: 'workload-identity-environment-conflict',
|
|
119
|
+
message: `workload environment already states ${conflicts.join(', ')}, which the runtime injects` };
|
|
120
|
+
}
|
|
121
|
+
const injected = {};
|
|
122
|
+
for (const key of identityKeys) {
|
|
123
|
+
injected[workloadReplicaIdentityEnvironmentNames[key]] = identity[key];
|
|
124
|
+
}
|
|
125
|
+
return { status: 'composed', environment: { ...environmentArg, ...injected } };
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* The replica identity a workload reads from its own environment (`process.env`).
|
|
129
|
+
*
|
|
130
|
+
* Reads the four injected names only, as own data properties, and nothing else of the environment.
|
|
131
|
+
* Throws naming every name that is missing and every name whose value is not a valid member, never
|
|
132
|
+
* a value, so a workload refuses to start with the exact variable its runtime did not inject.
|
|
133
|
+
*/
|
|
134
|
+
export const readWorkloadReplicaIdentityEnvironment = (environmentArg) => {
|
|
135
|
+
const missing = [];
|
|
136
|
+
const invalid = [];
|
|
137
|
+
const identity = {};
|
|
138
|
+
for (const key of identityKeys) {
|
|
139
|
+
const name = workloadReplicaIdentityEnvironmentNames[key];
|
|
140
|
+
const descriptor = Object.getOwnPropertyDescriptor(environmentArg, name);
|
|
141
|
+
if (descriptor === undefined || !Object.hasOwn(descriptor, 'value') || typeof descriptor.value !== 'string') {
|
|
142
|
+
missing.push(name);
|
|
143
|
+
}
|
|
144
|
+
else if (!memberRules[key].accepts(descriptor.value)) {
|
|
145
|
+
invalid.push(name);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
identity[key] = descriptor.value;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (missing.length > 0 || invalid.length > 0) {
|
|
152
|
+
throw new Error(`workload replica identity environment ${[
|
|
153
|
+
...missing.length > 0 ? [`lacks ${missing.join(', ')}`] : [],
|
|
154
|
+
...invalid.length > 0 ? [`states an invalid ${invalid.join(', ')}`] : [],
|
|
155
|
+
].join('; ')}`);
|
|
156
|
+
}
|
|
157
|
+
return snapshotWorkloadReplicaIdentity(identity);
|
|
158
|
+
};
|
|
159
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ya2xvYWRyZXBsaWNhaWRlbnRpdHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL3dvcmtsb2FkcmVwbGljYWlkZW50aXR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUVyRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUseUJBQXlCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN2RixPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUUzRCxPQUFPLEVBQUUsWUFBWSxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFxQmhGOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSx1Q0FBdUMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ25FLFNBQVMsRUFBRSwrQkFBK0I7SUFDMUMsU0FBUyxFQUFFLCtCQUErQjtJQUMxQyxTQUFTLEVBQUUsK0JBQStCO0lBQzFDLFdBQVcsRUFBRSxpQ0FBaUM7Q0FDNkIsQ0FBQyxDQUFDO0FBSy9FLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQ2hDLE1BQU0sQ0FBQyxJQUFJLENBQUMsdUNBQXVDLENBQXVDLENBQzNGLENBQUM7QUFDRixNQUFNLHdCQUF3QixHQUFzQixNQUFNLENBQUMsTUFBTSxDQUMvRCxNQUFNLENBQUMsTUFBTSxDQUFDLHVDQUF1QyxDQUFDLENBQ3ZELENBQUM7QUFFRjs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sK0JBQStCLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUMzRCwwQkFBMEIsRUFBRSxZQUFZLENBQUMsTUFBTTtJQUMvQyxpQ0FBaUMsRUFBRSxxQkFBcUIsQ0FBQyx5QkFBeUIsR0FBRyxZQUFZLENBQUMsTUFBTTtDQUN6RyxDQUFDLENBQUM7QUFFSCxNQUFNLFlBQVksR0FBRyxDQUFDLFFBQWlCLEVBQXNC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
|
|
@@ -2,6 +2,7 @@ import * as plugins from '../plugins.js';
|
|
|
2
2
|
import type { IIdentity, IIdentityCredential, IServiceDeploymentGrant } from '../data/user.js';
|
|
3
3
|
import type { IServiceMachineCredentialMetadata, IServiceMachineCredentialMutationResult, TServiceMachineCredentialMutation } from '../data/servicecredential.js';
|
|
4
4
|
import type { IDeploymentMachineUserMetadata, IDeploymentMachineUserMutationResult, TDeploymentMachineUserMutation } from '../data/deploymentmachineuser.js';
|
|
5
|
+
import type { IDelegatedAdminTokenMetadata, IDelegatedAdminTokenMint, IDelegatedAdminTokenMintResult } from '../data/delegatedadmintoken.js';
|
|
5
6
|
export interface IReq_Admin_GetServiceMachineCredential extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_GetServiceMachineCredential> {
|
|
6
7
|
method: 'getServiceMachineCredential';
|
|
7
8
|
request: {
|
|
@@ -175,3 +176,54 @@ export interface IReq_Cloudly_Coreflow_ReconcileBaseServices extends plugins.typ
|
|
|
175
176
|
routingProof?: ICoreflowRoutingProofResult;
|
|
176
177
|
};
|
|
177
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Mints one delegated admin token (`delegatedAdminTokenContract`) for what the request states
|
|
181
|
+
* (`IDelegatedAdminTokenMint`: its scopes, optional `lifetimeMs` and `secretKeyPrefixes`). Requires
|
|
182
|
+
* a human administrator who signed in within five minutes; a delegated session is refused. The
|
|
183
|
+
* plaintext token is answered on this answer only and is never retrievable again, so the request is
|
|
184
|
+
* sent once and its answer goes past every traffic hook. `lifetimeMs` absent is
|
|
185
|
+
* `defaultLifetimeMs`; Cloudly refuses any lifetime above `maximumLifetimeMs`. Refusals travel as
|
|
186
|
+
* `IDelegatedAdminTokenErrorData`.
|
|
187
|
+
*/
|
|
188
|
+
export interface IReq_Admin_MintDelegatedAdminToken extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_MintDelegatedAdminToken> {
|
|
189
|
+
method: 'mintDelegatedAdminToken';
|
|
190
|
+
request: {
|
|
191
|
+
identity: IIdentityCredential;
|
|
192
|
+
} & IDelegatedAdminTokenMint;
|
|
193
|
+
response: IDelegatedAdminTokenMintResult;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Lists the active delegated admin tokens, value-free, newest first: not expired, not revoked,
|
|
197
|
+
* and their administrator still a human administrator at the credential generation each was
|
|
198
|
+
* minted under. At most `delegatedAdminTokenContract.maximumListedTokens` are answered;
|
|
199
|
+
* `truncated` is true exactly when active tokens beyond them were left out, so no token that
|
|
200
|
+
* needs revoking is hidden without notice. Requires an administrator; a delegated session is
|
|
201
|
+
* refused. Refusals travel as `IDelegatedAdminTokenErrorData`.
|
|
202
|
+
*/
|
|
203
|
+
export interface IReq_Admin_GetDelegatedAdminTokens extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_GetDelegatedAdminTokens> {
|
|
204
|
+
method: 'getDelegatedAdminTokens';
|
|
205
|
+
request: {
|
|
206
|
+
identity: IIdentityCredential;
|
|
207
|
+
};
|
|
208
|
+
response: {
|
|
209
|
+
tokens: IDelegatedAdminTokenMetadata[];
|
|
210
|
+
truncated: boolean;
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Revokes one active delegated admin token; once the revocation commits, no use of the token
|
|
215
|
+
* commits after it, and every session exchanged for it is refused. A success has committed.
|
|
216
|
+
* Requires an administrator, the minting one or any other; a delegated session is refused.
|
|
217
|
+
* Refusals travel as `IDelegatedAdminTokenErrorData`: a token that already does not act (revoked,
|
|
218
|
+
* expired, or its administrator's credential or role changed) is `DELEGATED_ADMIN_TOKEN_INACTIVE`.
|
|
219
|
+
*/
|
|
220
|
+
export interface IReq_Admin_RevokeDelegatedAdminToken extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_RevokeDelegatedAdminToken> {
|
|
221
|
+
method: 'revokeDelegatedAdminToken';
|
|
222
|
+
request: {
|
|
223
|
+
identity: IIdentityCredential;
|
|
224
|
+
tokenId: string;
|
|
225
|
+
};
|
|
226
|
+
response: {
|
|
227
|
+
metadata: IDelegatedAdminTokenMetadata;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as plugins from '../plugins.js';
|
|
2
2
|
import type { ICoreMailEnvelope, ICoreMailInboundDeliveryPage, ICoreMailDownloadGrant, ICoreMailGatewayMessageDescriptor, ICoreMailGatewayOutboundStatus, ICoreMailInboundDelivery, ICoreMailOutboundMessageDescriptor, ICoreMailRecipientResolution, ICoreMailReconciliationStatus, ICoreMailReplicaIdentity, ICoreMailServiceMailStatistics, ICoreMailSubmission, ICoreMailUploadGrant, TCoreMailInboundAckOutcome, TCoreMailSha256, TCoreMailWorkloadOperation } from '../data/coremail.js';
|
|
3
3
|
import type { IMailConnectionInfo } from '../data/mail.js';
|
|
4
|
+
import type { IWorkloadReplicaIdentity } from '../data/workloadreplicaidentity.js';
|
|
4
5
|
export interface IReq_CoreMailAuthenticateWorkload extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_CoreMailAuthenticateWorkload> {
|
|
5
6
|
method: 'coreMailAuthenticateWorkload';
|
|
6
7
|
request: {
|
|
@@ -125,7 +126,11 @@ export interface IReq_CoreMailAuthenticateControl extends plugins.typedrequestIn
|
|
|
125
126
|
authenticated: true;
|
|
126
127
|
credentialId: string;
|
|
127
128
|
credentialVersion: number;
|
|
128
|
-
|
|
129
|
+
/**
|
|
130
|
+
* The authenticated replica: `IWorkloadReplicaIdentity` from CoreMail 34 on, the deprecated
|
|
131
|
+
* `ICoreMailReplicaIdentity` from an earlier CoreMail (`normalizeCoreMailReplicaIdentity`).
|
|
132
|
+
*/
|
|
133
|
+
replica: IWorkloadReplicaIdentity | ICoreMailReplicaIdentity;
|
|
129
134
|
};
|
|
130
135
|
}
|
|
131
136
|
export interface IReq_CoreMailPrepareDesiredStateUpload extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_CoreMailPrepareDesiredStateUpload> {
|
|
@@ -2,6 +2,10 @@ import * as plugins from '../plugins.js';
|
|
|
2
2
|
import * as userInterfaces from '../data/user.js';
|
|
3
3
|
/**
|
|
4
4
|
* get the identity that then will be used to get the config
|
|
5
|
+
*
|
|
6
|
+
* A delegated admin token (`delegatedAdminTokenContract`) is exchanged here too. Its identity is
|
|
7
|
+
* `type: 'machine'`, `role: 'api'`, `userId: 'delegated-admin-token:<tokenId>'`, expiring with the
|
|
8
|
+
* token; only the requests of the token's scopes admit it, and each re-reads the token.
|
|
5
9
|
*/
|
|
6
10
|
export interface IRequest_Any_Cloudly_CoreflowManager_GetIdentityByToken extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IRequest_Any_Cloudly_CoreflowManager_GetIdentityByToken> {
|
|
7
11
|
method: 'getIdentityByToken';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/interfaces",
|
|
3
|
-
"version": "32.
|
|
3
|
+
"version": "32.62.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
|
|
6
6
|
"exports": {
|
|
@@ -27,10 +27,7 @@
|
|
|
27
27
|
"@tsclass/tsclass": "^9.5.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@git.zone/
|
|
31
|
-
"@git.zone/tsdoc": "^3.0.2",
|
|
32
|
-
"@git.zone/tsrun": "^3.0.3",
|
|
33
|
-
"@git.zone/tstest": "^8.0.0",
|
|
30
|
+
"@git.zone/tsrepo": "^1.1.1",
|
|
34
31
|
"@types/node": "26.6.5",
|
|
35
32
|
"typescript": "^6.0.3"
|
|
36
33
|
},
|
|
@@ -64,8 +61,8 @@
|
|
|
64
61
|
"last 1 chrome versions"
|
|
65
62
|
],
|
|
66
63
|
"scripts": {
|
|
67
|
-
"test": "
|
|
68
|
-
"build": "
|
|
69
|
-
"buildDocs": "
|
|
64
|
+
"test": "tsrepo test test/ --verbose --logfile --timeout 60",
|
|
65
|
+
"build": "tsrepo build tsfolders",
|
|
66
|
+
"buildDocs": "tsrepo docs"
|
|
70
67
|
}
|
|
71
68
|
}
|
package/readme.md
CHANGED
|
@@ -1370,6 +1370,60 @@ Version 29 widens these numeric TypeScript fields to include those explicit null
|
|
|
1370
1370
|
policies; consumers must handle them before accepting configurations from v29.
|
|
1371
1371
|
Existing numeric configurations retain their exact meaning and digest.
|
|
1372
1372
|
|
|
1373
|
+
### Workload Replica Identity
|
|
1374
|
+
|
|
1375
|
+
Every runtime (Pallet, Onebox) injects the identity of the replica it runs into every workload,
|
|
1376
|
+
whether the workload reads it or not. `data.IWorkloadReplicaIdentity` is exactly
|
|
1377
|
+
`{ serviceId, replicaId, attemptId, imageDigest }`, the runtime assignment's own values:
|
|
1378
|
+
`data.buildWorkloadReplicaIdentity(assignment)` reads them from the admitted assignment through its
|
|
1379
|
+
exact schema, so the answer always validates. `attemptId` is the one execution attempt; a rollout or
|
|
1380
|
+
a replacement is a new attempt, and the controller's `attemptId` already binds the admitting
|
|
1381
|
+
rollout, so no rollout name or generation travels. `data.validateWorkloadReplicaIdentity` names
|
|
1382
|
+
every violation and `data.snapshotWorkloadReplicaIdentity` answers a detached frozen copy or throws.
|
|
1383
|
+
|
|
1384
|
+
`data.workloadReplicaIdentityEnvironmentNames` is the one spelling of the injected names:
|
|
1385
|
+
`SERVEZONE_WORKLOAD_SERVICE_ID`, `SERVEZONE_WORKLOAD_REPLICA_ID`, `SERVEZONE_WORKLOAD_ATTEMPT_ID` and
|
|
1386
|
+
`SERVEZONE_WORKLOAD_IMAGE_DIGEST`. They sit under the platform-reserved `SERVEZONE_` prefix
|
|
1387
|
+
(`platformReservedSecretKeyPrefixes`). A runtime composes what a replica runs with through
|
|
1388
|
+
`data.composeWorkloadReplicaEnvironment(configEnvironment, identity)`: the config's environment
|
|
1389
|
+
with the four names added, or `refused` with `workload-identity-environment-conflict` and a message
|
|
1390
|
+
naming every colliding key and no value (`TWorkloadReplicaEnvironmentComposition`). An identity that
|
|
1391
|
+
is not exactly one is thrown. A workload reads its identity with
|
|
1392
|
+
`data.readWorkloadReplicaIdentityEnvironment(process.env)`, which reads the four names only and
|
|
1393
|
+
throws naming every missing or invalid variable, never a value, so a workload refuses to start
|
|
1394
|
+
naming the variable its runtime did not inject.
|
|
1395
|
+
|
|
1396
|
+
Cloudly refuses a service whose effective environment (image and service values) or one of whose
|
|
1397
|
+
launcher-environment secrets states an injected name, before any runtime sees it:
|
|
1398
|
+
`data.findWorkloadReplicaIdentityEnvironmentConflicts(names)` answers the colliding names among
|
|
1399
|
+
the environment's keys and the secrets' variable names, and the service's status reads
|
|
1400
|
+
`workload-identity-environment-conflict`. Secrets count because WorkloadInit sets a secret over a
|
|
1401
|
+
plain variable of the same name.
|
|
1402
|
+
|
|
1403
|
+
Only Cloudly states `workload-identity-environment-conflict` on the wire, in the service status it
|
|
1404
|
+
stores and answers on `getServiceRuntimeSpec`, `getServiceRuntimeSpecForCluster` and
|
|
1405
|
+
`getServiceRuntimeSpecs`. Those requests carry no protocol offer, so the reason cannot be gated on
|
|
1406
|
+
a reader's version, and no other refusal names this check. A reader built before the interfaces
|
|
1407
|
+
release that introduced this contract refuses such a status whole, so every client that reads
|
|
1408
|
+
statuses moves to that release before Cloudly does, and a Cloudly that has stored one is not rolled
|
|
1409
|
+
back below it. A node never states the reason: a Pallet whose composition refuses (only a Cloudly
|
|
1410
|
+
before this contract places such a run) reports the run as the existing
|
|
1411
|
+
`TRuntimeAssignmentFailureCause` `{ kind: 'refused', reason: 'invalid' }`, which every reader since
|
|
1412
|
+
32.45.0 reads and a controller never re-places with an identical configuration.
|
|
1413
|
+
|
|
1414
|
+
`IRuntimeConfig.environment` keeps its bound, `runtimeConfigContract.maximumEnvironmentEntries`
|
|
1415
|
+
(128). A runtime's executor admits the config's entries plus the injected ones:
|
|
1416
|
+
`data.workloadReplicaIdentityContract.maximumExecutorEnvironmentEntries`, computed as that bound
|
|
1417
|
+
plus `injectedEnvironmentEntries` (4), never a literal of the runtime's own. Pallet's native
|
|
1418
|
+
invocation check (`rust/src/execution.rs`, today a literal 128) admits exactly this value, and its
|
|
1419
|
+
TypeScript execution owner hands the composed environment to it in place of `config.environment`.
|
|
1420
|
+
|
|
1421
|
+
Injection is gated by the release, not per peer: a Pallet moves to the interfaces release that
|
|
1422
|
+
introduced this contract together with the injection, so a Pallet speaking it injects. Cloudly
|
|
1423
|
+
raises the `minimumPeerVersion` of its Pallet runtime sessions to that release (peers first, see
|
|
1424
|
+
`IProtocolOffer`) in the commit that first deploys a workload requiring its identity, such as
|
|
1425
|
+
CoreMail 34. No controller or runtime keeps a per-version send branch.
|
|
1426
|
+
|
|
1373
1427
|
### Authenticated Runtime Sessions
|
|
1374
1428
|
|
|
1375
1429
|
`requests.runtimesession` defines `registerPalletRuntimeSession`,
|
|
@@ -2106,7 +2160,8 @@ evidence has not arrived). `refused` carries a `TServiceRuntimeRefusal`:
|
|
|
2106
2160
|
`volume-initialization-unknown`, `volume-ownership-unknown`, `launcher-secret-ownership-unknown`,
|
|
2107
2161
|
`objectstorage-endpoint-missing`, `objectstorage-binding-invalid`, `objectstorage-environment-conflict`,
|
|
2108
2162
|
`objectstorage-endpoint-unselected`, `database-endpoint-missing`, `database-endpoint-unselected`,
|
|
2109
|
-
`cluster-relay-missing
|
|
2163
|
+
`cluster-relay-missing`, `cluster-ingress-environment-conflict` or
|
|
2164
|
+
`workload-identity-environment-conflict`.
|
|
2110
2165
|
The first four volume
|
|
2111
2166
|
refusals name what a local storage claim cannot express: a volume driver other than a node-local
|
|
2112
2167
|
directory, driver or mount options, one volume mounted by more than one replica or service, and a
|
|
@@ -2123,7 +2178,10 @@ designated ingress service, whose environment `composeClusterIngressEnvironment`
|
|
|
2123
2178
|
[Cluster Ingress](#cluster-ingress): `cluster-relay-missing` is a cluster that states no valid
|
|
2124
2179
|
relay, which an administrator enables with `enableClusterRelay`, and
|
|
2125
2180
|
`cluster-ingress-environment-conflict` an ingress service whose own environment already sets one of
|
|
2126
|
-
`clusterIngressEnvironmentNames`. `
|
|
2181
|
+
`clusterIngressEnvironmentNames`. `workload-identity-environment-conflict` is a service whose
|
|
2182
|
+
effective environment, or one of whose launcher-environment secrets, states one of the names every
|
|
2183
|
+
runtime injects, see [Workload Replica Identity](#workload-replica-identity), which also states why
|
|
2184
|
+
every reader of statuses moves to its release before Cloudly does. `ports-too-many` is a service declaring more target ports than the
|
|
2127
2185
|
`runtimeConfigContract.maximumTargetPorts` (64) a config carries, where `port-protocol-unsupported`
|
|
2128
2186
|
is about one port's protocol; `node-authority-unsupported` is a node whose session resolved fewer
|
|
2129
2187
|
workload authorities than the spec needs, refused at the admit step rather than handed to a node
|
|
@@ -4158,11 +4216,70 @@ per-task authentication and readiness, then mark the previous verifier
|
|
|
4158
4216
|
`retiring` with `acceptUntil`. Remove the previous verifier and secret only
|
|
4159
4217
|
after its acceptance window has elapsed and no session uses that composite
|
|
4160
4218
|
credential identity.
|
|
4161
|
-
Reconciliation status
|
|
4162
|
-
|
|
4163
|
-
current
|
|
4164
|
-
|
|
4165
|
-
|
|
4219
|
+
Reconciliation status and the `coreMailAuthenticateControl` response carry the
|
|
4220
|
+
reporting replica so Coreflow can correlate every response with its
|
|
4221
|
+
authoritative current roster. CoreMail 34 and later report
|
|
4222
|
+
`data.IWorkloadReplicaIdentity` (`serviceId`, `replicaId`, `attemptId`,
|
|
4223
|
+
`imageDigest`), the identity its runtime injects; the attempt is what the roster
|
|
4224
|
+
correlates by. A CoreMail before 34 reports the deprecated
|
|
4225
|
+
`data.ICoreMailReplicaIdentity` (`taskId`, `serviceId`, `rolloutId`,
|
|
4226
|
+
`rolloutGeneration`, `imageDigest`), whose `taskId` is that attempt. Both types
|
|
4227
|
+
accept either shape, and `data.normalizeCoreMailReplicaIdentity` reads each
|
|
4228
|
+
exactly: a record stating `taskId` is the deprecated shape, any other the
|
|
4229
|
+
workload identity, and neither admits a member of the other. The deprecated
|
|
4230
|
+
shape is removed in the next major. Apply
|
|
4231
|
+
`data.normalizeCoreMailReconciliationStatus` before using pending-inbound or
|
|
4232
|
+
composite active-session counts for drain and rotation decisions.
|
|
4233
|
+
|
|
4234
|
+
#### CoreMail runtime environment (CoreMail 34 and later)
|
|
4235
|
+
|
|
4236
|
+
CoreMail 34 reads:
|
|
4237
|
+
|
|
4238
|
+
- its replica identity from `data.workloadReplicaIdentityEnvironmentNames`
|
|
4239
|
+
(`SERVEZONE_WORKLOAD_SERVICE_ID`, `SERVEZONE_WORKLOAD_REPLICA_ID`,
|
|
4240
|
+
`SERVEZONE_WORKLOAD_ATTEMPT_ID`, `SERVEZONE_WORKLOAD_IMAGE_DIGEST`) through
|
|
4241
|
+
`data.readWorkloadReplicaIdentityEnvironment`, and refuses to start naming a
|
|
4242
|
+
missing variable;
|
|
4243
|
+
- its database and object storage under the names its platform bindings deliver,
|
|
4244
|
+
`data.coreMailStorageEnvironmentNames`: `MONGODB_URL` and `MONGODB_DATABASE`
|
|
4245
|
+
(the Corestore database binding), `S3_ENDPOINT`, `S3_REGION` and `S3_BUCKET`
|
|
4246
|
+
(the object-storage binding) and `S3_ACCESS_KEY` and `S3_SECRET_KEY` (that
|
|
4247
|
+
binding's credential secrets);
|
|
4248
|
+
- `COREMAIL_CONTROL_BOOTSTRAP` (`data.coreMailRuntimeKeys.controlBootstrap`), and
|
|
4249
|
+
optionally `COREMAIL_PORT`, `COREMAIL_HOSTNAMES` and
|
|
4250
|
+
`COREMAIL_MONGODB_MAX_POOL_SIZE` (`port`, `hostnames`, `mongoDbMaxPoolSize`);
|
|
4251
|
+
- the secrets its desired state references by name, as before.
|
|
4252
|
+
|
|
4253
|
+
It no longer reads `COREMAIL_SERVICE_ID`, `COREMAIL_TASK_ID`,
|
|
4254
|
+
`COREMAIL_ROLLOUT_ID`, `COREMAIL_ROLLOUT_GENERATION`, `COREMAIL_IMAGE_DIGEST`,
|
|
4255
|
+
`COREMAIL_MONGODB_URL`, `COREMAIL_MONGODB_NAME`, `COREMAIL_STORAGE_DESCRIPTOR`
|
|
4256
|
+
or `COREMAIL_BUCKET_NAME`, and a provisioner stops writing them. CoreMail 34
|
|
4257
|
+
runs only on a runtime that injects its identity, see
|
|
4258
|
+
[Workload Replica Identity](#workload-replica-identity).
|
|
4259
|
+
|
|
4260
|
+
Readers first. A reader built before the interfaces release that introduced
|
|
4261
|
+
`data.IWorkloadReplicaIdentity` refuses CoreMail 34's replica whole, in the
|
|
4262
|
+
`coreMailAuthenticateControl` response and in reconciliation status alike: its
|
|
4263
|
+
`normalizeCoreMailReplicaIdentity` requires `taskId`, `rolloutId` and
|
|
4264
|
+
`rolloutGeneration`. The `minimumPeerVersion` of Pallet runtime sessions does not
|
|
4265
|
+
reach these readers, so the order is fixed: `@serve.zone/api`'s
|
|
4266
|
+
`CoreMailControlClient`, and with it Cloudly's CoreMail manager and Onebox's
|
|
4267
|
+
CoreMail provider, move to that interfaces release before any CoreMail 34 runs.
|
|
4268
|
+
Cloudly deploys CoreMail 34 only once its own reader is on that release, and
|
|
4269
|
+
Onebox moves its CoreMail pin to 34 only in the release that carries the
|
|
4270
|
+
upgraded reader. A CoreMail before 34 keeps writing the deprecated shape, which
|
|
4271
|
+
every reader reads as before.
|
|
4272
|
+
|
|
4273
|
+
In source, both carrying types and the normalizer's return value are
|
|
4274
|
+
`data.IWorkloadReplicaIdentity | data.ICoreMailReplicaIdentity`, a type-level
|
|
4275
|
+
change shipped in a 32.x minor. The consumers that read the replica,
|
|
4276
|
+
`@serve.zone/api` (`CoreMailControlClient`), Cloudly (CoreMail manager), Onebox
|
|
4277
|
+
(CoreMail provider) and CoreMail, narrow by `taskId`:
|
|
4278
|
+
|
|
4279
|
+
```typescript
|
|
4280
|
+
const replica = data.normalizeCoreMailReplicaIdentity(response.replica);
|
|
4281
|
+
const attemptId = 'taskId' in replica ? replica.taskId : replica.attemptId;
|
|
4282
|
+
```
|
|
4166
4283
|
|
|
4167
4284
|
#### CoreMail control-plane settings and operator RPCs
|
|
4168
4285
|
|
|
@@ -4276,6 +4393,35 @@ request fence, and the trusted local organization/cluster expectation. It never
|
|
|
4276
4393
|
decrypts. Organization and cluster are derived from the verified cluster JWT
|
|
4277
4394
|
and cannot be selected by request fields.
|
|
4278
4395
|
|
|
4396
|
+
### Service Mail Environment
|
|
4397
|
+
|
|
4398
|
+
`data.serviceMailEnvironmentNames` spells the environment a service's default
|
|
4399
|
+
sender is delivered under, once for the provisioners (Cloudly, Onebox) and the
|
|
4400
|
+
reader (`@serve.zone/platformclient`). It has three groups, each delivered
|
|
4401
|
+
whole: the typed dcrouter-direct binding (`MAIL_FROM`, `MAIL_TYPED_URL`,
|
|
4402
|
+
`MAIL_API_CREDENTIAL_ID`, `MAIL_API_CREDENTIAL_SECRET`), its SMTP compatibility
|
|
4403
|
+
surface (`SMTP_HOST`, `SMTP_PORT`, `SMTP_TLS_MODE`, `SMTP_USERNAME`,
|
|
4404
|
+
`SMTP_PASSWORD`) and a CoreMail binding (`MAIL_COREMAIL_URL`,
|
|
4405
|
+
`MAIL_COREMAIL_BINDING_ID`, `MAIL_COREMAIL_CREDENTIAL_ID`,
|
|
4406
|
+
`MAIL_COREMAIL_CREDENTIAL_VERSION`, `MAIL_COREMAIL_CREDENTIAL_SECRET`, with
|
|
4407
|
+
`MAIL_FROM`). The credential secrets and passwords are delivered as secrets.
|
|
4408
|
+
`data.createServiceMailAddressEnvironmentNames(token)` answers the same names
|
|
4409
|
+
for a further sender address, scoped to its upper-case address token
|
|
4410
|
+
(`MAIL_<TOKEN>_FROM`, `MAIL_<TOKEN>_TYPED_URL`, `MAIL_<TOKEN>_SMTP_HOST`,
|
|
4411
|
+
`MAIL_<TOKEN>_COREMAIL_URL`, …); any other token is thrown.
|
|
4412
|
+
|
|
4413
|
+
`MAIL_TYPED_URL` and every `MAIL_<TOKEN>_TYPED_URL` carry
|
|
4414
|
+
`ICloudlySettings.dcrouterMailEndpointUrl`, the canonical HTTPS URL of
|
|
4415
|
+
dcrouter's tenant mail listener, and nothing else: never `dcrouterGatewayUrl`,
|
|
4416
|
+
which is Cloudly's own gateway-client endpoint, and never a CoreMail endpoint,
|
|
4417
|
+
which travels as `MAIL_COREMAIL_URL`. `data.validateDcrouterMailEndpointUrl`
|
|
4418
|
+
accepts an `https:` URL without credentials, query or fragment, at most
|
|
4419
|
+
`data.dcrouterMailEndpointUrlMaximumLength` (2,048) characters, written exactly
|
|
4420
|
+
as `new URL(value).href` spells it, and names every violation without quoting
|
|
4421
|
+
the value. When the setting is absent, Cloudly refuses to provision a typed
|
|
4422
|
+
dcrouter-direct mail binding and names `dcrouterMailEndpointUrl` in the refusal;
|
|
4423
|
+
it falls back to no other endpoint.
|
|
4424
|
+
|
|
4279
4425
|
### Corestore Runtime Credentials
|
|
4280
4426
|
|
|
4281
4427
|
The Node-only `/runtime` export defines
|
|
@@ -4845,7 +4991,7 @@ pnpm test
|
|
|
4845
4991
|
pnpm run buildDocs
|
|
4846
4992
|
```
|
|
4847
4993
|
|
|
4848
|
-
The package is authored as ESM TypeScript and built with `
|
|
4994
|
+
The package is authored as ESM TypeScript and built with `tsrepo build tsfolders`.
|
|
4849
4995
|
|
|
4850
4996
|
## License and Legal Information
|
|
4851
4997
|
|