@serve.zone/interfaces 32.25.0 → 32.27.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.
@@ -1,5 +1,6 @@
1
1
  import type { IPlatformBinding } from './types.js';
2
2
  import type { IPlatformObjectStorageEndpoint, TObjectStorageEndpointEnvironment, TObjectStorageEndpointEnvironmentKey } from './objectstorage.js';
3
+ import type { TServiceRuntimeRefusal, TServiceRuntimeWaitingReason } from '../data/serviceruntime.js';
3
4
  import type { TObjectStorageRetentionEnvironment, TObjectStorageRetentionEnvironmentKey } from './objectstorageretention.js';
4
5
  /**
5
6
  * Every non-secret environment name an object-storage binding can give a workload: the nine
@@ -32,6 +33,9 @@ export type TObjectStorageBindingEnvironment = TObjectStorageEndpointEnvironment
32
33
  * The evidence's reconciliation generation and binding-request digest are read from its own
33
34
  * authority: they are what Cloudly checked when it admitted the evidence, and the binding document
34
35
  * does not repeat them. Everything the binding does state is checked again here.
36
+ *
37
+ * `composeObjectStorageServiceEnvironment()` answers the same questions with a service runtime
38
+ * reason instead of an error.
35
39
  */
36
40
  export declare const createObjectStorageBindingEnvironment: (bindingArg: IPlatformBinding, endpointArg: IPlatformObjectStorageEndpoint) => Promise<TObjectStorageBindingEnvironment>;
37
41
  /**
@@ -47,3 +51,58 @@ export declare const validateObjectStorageBindingEnvironment: (environmentArg: u
47
51
  * would otherwise have one value silently replace the other. The inputs are not changed.
48
52
  */
49
53
  export declare const mergeObjectStorageBindingEnvironment: (environmentArg: Readonly<Record<string, string>>, bindingEnvironmentArg: TObjectStorageBindingEnvironment) => Record<string, string>;
54
+ /** One enabled object-storage binding of a service and the endpoint of the node it is placed on. */
55
+ export interface IObjectStorageBindingEnvironmentSource {
56
+ binding: IPlatformBinding;
57
+ /**
58
+ * The `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on, `null`
59
+ * when that node states none. A binding placed on no node is not a source: its service waits as
60
+ * `bindings-pending`.
61
+ */
62
+ endpoint: IPlatformObjectStorageEndpoint | null;
63
+ }
64
+ /**
65
+ * The service runtime waiting reason an object-storage environment reports:
66
+ * `objectstorage-retention-pending`, a binding that declares compliance retention whose evidence
67
+ * has not arrived.
68
+ */
69
+ export type TObjectStorageEnvironmentWaitingReason = Extract<TServiceRuntimeWaitingReason, 'objectstorage-retention-pending'>;
70
+ /**
71
+ * The service runtime refusals an object-storage environment reports.
72
+ * `objectstorage-endpoint-missing` is a binding whose node states no valid
73
+ * `corestoreObjectStorage`; `objectstorage-binding-invalid` a binding
74
+ * `createObjectStorageBindingEnvironment()` refuses (not `objectstorage`, no canonical bucket, no
75
+ * canonical retention intent, or evidence that does not prove this binding);
76
+ * `objectstorage-environment-conflict` a name the service's own environment or another binding
77
+ * already sets.
78
+ */
79
+ export type TObjectStorageEnvironmentRefusal = Extract<TServiceRuntimeRefusal, 'objectstorage-endpoint-missing' | 'objectstorage-binding-invalid' | 'objectstorage-environment-conflict'>;
80
+ /** A service's environment with every object-storage binding added, or the one reason it is not. */
81
+ export type TObjectStorageServiceEnvironmentComposition = {
82
+ status: 'composed';
83
+ environment: Record<string, string>;
84
+ } | {
85
+ status: 'waiting';
86
+ reason: TObjectStorageEnvironmentWaitingReason;
87
+ bindingId: string;
88
+ message: string;
89
+ } | {
90
+ status: 'refused';
91
+ reason: TObjectStorageEnvironmentRefusal;
92
+ bindingId: string;
93
+ message: string;
94
+ };
95
+ /**
96
+ * The service's own environment with the environment of every enabled object-storage binding
97
+ * added, or the service runtime reason it cannot be — so a producer reports the contract's
98
+ * verdict instead of classifying its errors.
99
+ *
100
+ * Each binding's names are `createObjectStorageBindingEnvironment()`'s from its node's endpoint,
101
+ * added as `mergeObjectStorageBindingEnvironment()` adds them. Bindings are taken in id order, so
102
+ * the verdict does not depend on the order of `sourcesArg`. A refusal outranks a wait, because a
103
+ * wait resolves on its own and a refusal never does: a binding waiting for retention evidence
104
+ * already claims all twenty of its names, so a collision with it is refused now, not once its
105
+ * evidence arrives. The first refusal is answered, otherwise the first wait, otherwise the
106
+ * composed environment. The inputs are not changed.
107
+ */
108
+ export declare const composeObjectStorageServiceEnvironment: (serviceEnvironmentArg: Readonly<Record<string, string>>, sourcesArg: readonly IObjectStorageBindingEnvironmentSource[]) => Promise<TObjectStorageServiceEnvironmentComposition>;
@@ -1,5 +1,5 @@
1
- import { createObjectStorageEndpointEnvironment, objectStorageEndpointEnvironmentKeys, validateObjectStorageEndpointEnvironment, validatePlatformObjectStorageBucketName, } from './objectstorage.js';
2
- import { createObjectStorageRetentionEnvironment, objectStorageRetentionEnvironmentKeys, validateObjectStorageRetentionEnvironment, validateObjectStorageRetentionIntent, } from './objectstorageretention.js';
1
+ import { createObjectStorageEndpointEnvironment, objectStorageEndpointEnvironmentKeys, validateObjectStorageEndpointEnvironment, validatePlatformObjectStorageBucketName, validatePlatformObjectStorageEndpoint, } from './objectstorage.js';
2
+ import { createObjectStorageRetentionEnvironment, objectStorageRetentionEnvironmentKeys, validateObjectStorageRetentionEnvironment, validateObjectStorageRetentionEvidence, validateObjectStorageRetentionIntent, } from './objectstorageretention.js';
3
3
  import { isPlainRecord, readDataProperties } from '../private/dataproperties.js';
4
4
  /**
5
5
  * Every non-secret environment name an object-storage binding can give a workload: the nine
@@ -10,65 +10,98 @@ export const objectStorageBindingEnvironmentKeys = Object.freeze([
10
10
  ...objectStorageRetentionEnvironmentKeys,
11
11
  ]);
12
12
  /**
13
- * The environment Cloudly composes into a workload's runtime configuration for one binding.
14
- *
15
- * `bindingArg` is the Cloudly binding document and `endpointArg` the
16
- * `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on. Refused by
17
- * name, never guessed:
18
- *
19
- * - a binding that is not `objectstorage` or carries no canonical `objectstorageBucketName`;
20
- * - an endpoint that `validatePlatformObjectStorageEndpoint()` refuses;
21
- * - a binding that declares `objectstorageRetention` without `evidence`. Its bucket's retention is
22
- * not proven yet, so the workload is not given an environment that would let it run as if its
23
- * bucket were ordinary storage; the caller waits for the evidence.
24
- * - evidence that does not prove the binding's own service, binding id, bucket and intent.
25
- *
26
- * A binding without `objectstorageRetention` gets the endpoint names alone: it promised no
27
- * retention, so there is nothing to state.
28
- *
29
- * The evidence's reconciliation generation and binding-request digest are read from its own
30
- * authority: they are what Cloudly checked when it admitted the evidence, and the binding document
31
- * does not repeat them. Everything the binding does state is checked again here.
13
+ * The one place a binding's environment is decided. `createObjectStorageBindingEnvironment()`
14
+ * throws its message, `composeObjectStorageServiceEnvironment()` reports its reason.
32
15
  */
33
- export const createObjectStorageBindingEnvironment = async (bindingArg, endpointArg) => {
16
+ const resolveBindingEnvironment = async (bindingArg, endpointArg) => {
34
17
  const binding = readDataProperties(bindingArg);
35
18
  if (binding === null || binding.capability !== 'objectstorage') {
36
- throw new Error('objectstorage binding environment requires an objectstorage binding');
19
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
20
+ message: 'objectstorage binding environment requires an objectstorage binding' };
37
21
  }
38
22
  const bindingName = typeof binding.id === 'string' ? binding.id : '(unnamed)';
39
23
  if (validatePlatformObjectStorageBucketName(binding.objectstorageBucketName).length > 0) {
40
- throw new Error(`objectstorage binding ${bindingName} carries no canonical bucket name; Cloudly owns it and `
41
- + 'no environment invents one');
24
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
25
+ message: `objectstorage binding ${bindingName} carries no canonical bucket name; Cloudly owns `
26
+ + 'it and no environment invents one' };
27
+ }
28
+ if (endpointArg === null) {
29
+ return { status: 'refused', reason: 'objectstorage-endpoint-missing',
30
+ message: `objectstorage binding ${bindingName} is placed on a node that states no `
31
+ + 'corestoreObjectStorage endpoint' };
32
+ }
33
+ const endpointErrors = validatePlatformObjectStorageEndpoint(endpointArg);
34
+ if (endpointErrors.length > 0) {
35
+ return { status: 'refused', reason: 'objectstorage-endpoint-missing', message: endpointErrors[0] };
42
36
  }
43
37
  const bucketName = binding.objectstorageBucketName;
44
38
  const endpointEnvironment = createObjectStorageEndpointEnvironment(endpointArg, bucketName);
45
39
  const retention = binding.objectstorageRetention;
46
40
  if (retention === undefined) {
47
- return endpointEnvironment;
41
+ return { status: 'composed', environment: endpointEnvironment };
48
42
  }
49
43
  const retentionBinding = readDataProperties(retention);
50
44
  if (retentionBinding === null
51
45
  || validateObjectStorageRetentionIntent(retentionBinding.intent).length > 0) {
52
- throw new Error(`objectstorage binding ${bindingName} must declare canonical compliance-retention intent`);
46
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
47
+ message: `objectstorage binding ${bindingName} must declare canonical compliance-retention `
48
+ + 'intent' };
53
49
  }
54
50
  const evidence = retentionBinding.evidence;
55
51
  if (evidence === undefined) {
56
- throw new Error(`objectstorage binding ${bindingName} declares compliance retention without verified `
57
- + 'evidence; its workload environment waits for the evidence');
52
+ return { status: 'waiting', reason: 'objectstorage-retention-pending',
53
+ message: `objectstorage binding ${bindingName} declares compliance retention without `
54
+ + 'verified evidence; its workload environment waits for the evidence' };
58
55
  }
59
56
  const authority = isPlainRecord(evidence) ? readDataProperties(evidence.authority) : null;
60
57
  if (authority === null) {
61
- throw new Error('objectstorage retention evidence must use its exact schema');
58
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
59
+ message: 'objectstorage retention evidence must use its exact schema' };
62
60
  }
63
- const retentionEnvironment = await createObjectStorageRetentionEnvironment(evidence, {
61
+ const expectation = {
64
62
  serviceId: binding.serviceId,
65
63
  bindingId: binding.id,
66
64
  reconciliationGeneration: authority.reconciliationGeneration,
67
65
  bindingRequestDigest: authority.bindingRequestDigest,
68
66
  bucketName,
69
67
  intent: retentionBinding.intent,
70
- });
71
- return { ...endpointEnvironment, ...retentionEnvironment };
68
+ };
69
+ const evidenceErrors = await validateObjectStorageRetentionEvidence(evidence, expectation);
70
+ if (evidenceErrors.length > 0) {
71
+ return { status: 'refused', reason: 'objectstorage-binding-invalid', message: evidenceErrors[0] };
72
+ }
73
+ const retentionEnvironment = await createObjectStorageRetentionEnvironment(evidence, expectation);
74
+ return { status: 'composed', environment: { ...endpointEnvironment, ...retentionEnvironment } };
75
+ };
76
+ /**
77
+ * The environment Cloudly composes into a workload's runtime configuration for one binding.
78
+ *
79
+ * `bindingArg` is the Cloudly binding document and `endpointArg` the
80
+ * `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on. Refused by
81
+ * name, never guessed:
82
+ *
83
+ * - a binding that is not `objectstorage` or carries no canonical `objectstorageBucketName`;
84
+ * - an endpoint that `validatePlatformObjectStorageEndpoint()` refuses;
85
+ * - a binding that declares `objectstorageRetention` without `evidence`. Its bucket's retention is
86
+ * not proven yet, so the workload is not given an environment that would let it run as if its
87
+ * bucket were ordinary storage; the caller waits for the evidence.
88
+ * - evidence that does not prove the binding's own service, binding id, bucket and intent.
89
+ *
90
+ * A binding without `objectstorageRetention` gets the endpoint names alone: it promised no
91
+ * retention, so there is nothing to state.
92
+ *
93
+ * The evidence's reconciliation generation and binding-request digest are read from its own
94
+ * authority: they are what Cloudly checked when it admitted the evidence, and the binding document
95
+ * does not repeat them. Everything the binding does state is checked again here.
96
+ *
97
+ * `composeObjectStorageServiceEnvironment()` answers the same questions with a service runtime
98
+ * reason instead of an error.
99
+ */
100
+ export const createObjectStorageBindingEnvironment = async (bindingArg, endpointArg) => {
101
+ const resolution = await resolveBindingEnvironment(bindingArg, endpointArg);
102
+ if (resolution.status !== 'composed')
103
+ throw new Error(resolution.message);
104
+ return resolution.environment;
72
105
  };
73
106
  /**
74
107
  * Validates one binding's environment: the nine endpoint names, the eleven retention names all or
@@ -99,6 +132,9 @@ export const validateObjectStorageBindingEnvironment = async (environmentArg, pa
99
132
  }
100
133
  return errors;
101
134
  };
135
+ const findEnvironmentCollisions = (claimedKeysArg, keysArg) => keysArg.filter((keyArg) => claimedKeysArg.has(keyArg)).sort();
136
+ const describeEnvironmentCollisions = (collisionsArg) => (`objectstorage binding environment collides with ${collisionsArg.join(', ')} already set on the `
137
+ + 'workload');
102
138
  /**
103
139
  * A workload environment with one binding's environment added.
104
140
  *
@@ -107,13 +143,52 @@ export const validateObjectStorageBindingEnvironment = async (environmentArg, pa
107
143
  * would otherwise have one value silently replace the other. The inputs are not changed.
108
144
  */
109
145
  export const mergeObjectStorageBindingEnvironment = (environmentArg, bindingEnvironmentArg) => {
110
- const collisions = Object.keys(bindingEnvironmentArg)
111
- .filter((keyArg) => Object.hasOwn(environmentArg, keyArg))
112
- .sort();
113
- if (collisions.length > 0) {
114
- throw new Error(`objectstorage binding environment collides with ${collisions.join(', ')} already set on the `
115
- + 'workload');
116
- }
146
+ const collisions = findEnvironmentCollisions(new Set(Object.keys(environmentArg)), Object.keys(bindingEnvironmentArg));
147
+ if (collisions.length > 0)
148
+ throw new Error(describeEnvironmentCollisions(collisions));
117
149
  return { ...environmentArg, ...bindingEnvironmentArg };
118
150
  };
119
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2JqZWN0c3RvcmFnZWVudmlyb25tZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcGxhdGZvcm0vb2JqZWN0c3RvcmFnZWVudmlyb25tZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQU1BLE9BQU8sRUFDTCxzQ0FBc0MsRUFDdEMsb0NBQW9DLEVBQ3BDLHdDQUF3QyxFQUN4Qyx1Q0FBdUMsR0FDeEMsTUFBTSxvQkFBb0IsQ0FBQztBQU81QixPQUFPLEVBQ0wsdUNBQXVDLEVBQ3ZDLHFDQUFxQyxFQUNyQyx5Q0FBeUMsRUFDekMsb0NBQW9DLEdBQ3JDLE1BQU0sNkJBQTZCLENBQUM7QUFDckMsT0FBTyxFQUFFLGFBQWEsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRWpGOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLG1DQUFtQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDL0QsR0FBRyxvQ0FBb0M7SUFDdkMsR0FBRyxxQ0FBcUM7Q0FDaEMsQ0FBQyxDQUFDO0FBY1o7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBQ0gsTUFBTSxDQUFDLE1BQU0scUNBQXFDLEdBQUcsS0FBSyxFQUN4RCxVQUE0QixFQUM1QixXQUEyQyxFQUNBLEVBQUU7SUFDN0MsTUFBTSxPQUFPLEdBQUcsa0JBQWtCLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDL0MsSUFBSSxPQUFPLEtBQUssSUFBSSxJQUFJLE9BQU8sQ0FBQyxVQUFVLEtBQUssZUFBZSxFQUFFLENBQUM7UUFDL0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxxRUFBcUUsQ0FBQyxDQUFDO0lBQ3pGLENBQUM7SUFDRCxNQUFNLFdBQVcsR0FBRyxPQUFPLE9BQU8sQ0FBQyxFQUFFLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUM7SUFDOUUsSUFBSSx1Q0FBdUMsQ0FBQyxPQUFPLENBQUMsdUJB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
151
+ /**
152
+ * The service's own environment with the environment of every enabled object-storage binding
153
+ * added, or the service runtime reason it cannot be — so a producer reports the contract's
154
+ * verdict instead of classifying its errors.
155
+ *
156
+ * Each binding's names are `createObjectStorageBindingEnvironment()`'s from its node's endpoint,
157
+ * added as `mergeObjectStorageBindingEnvironment()` adds them. Bindings are taken in id order, so
158
+ * the verdict does not depend on the order of `sourcesArg`. A refusal outranks a wait, because a
159
+ * wait resolves on its own and a refusal never does: a binding waiting for retention evidence
160
+ * already claims all twenty of its names, so a collision with it is refused now, not once its
161
+ * evidence arrives. The first refusal is answered, otherwise the first wait, otherwise the
162
+ * composed environment. The inputs are not changed.
163
+ */
164
+ export const composeObjectStorageServiceEnvironment = async (serviceEnvironmentArg, sourcesArg) => {
165
+ const sources = [...sourcesArg].sort((leftArg, rightArg) => (leftArg.binding.id < rightArg.binding.id ? -1 : leftArg.binding.id > rightArg.binding.id ? 1 : 0));
166
+ let environment = { ...serviceEnvironmentArg };
167
+ const claimedKeys = new Set(Object.keys(environment));
168
+ let wait = null;
169
+ for (const source of sources) {
170
+ const bindingId = source.binding.id;
171
+ const resolution = await resolveBindingEnvironment(source.binding, source.endpoint);
172
+ if (resolution.status === 'refused') {
173
+ return { status: 'refused', reason: resolution.reason, bindingId, message: resolution.message };
174
+ }
175
+ const keys = resolution.status === 'composed'
176
+ ? Object.keys(resolution.environment)
177
+ : objectStorageBindingEnvironmentKeys;
178
+ const collisions = findEnvironmentCollisions(claimedKeys, keys);
179
+ if (collisions.length > 0) {
180
+ return { status: 'refused', reason: 'objectstorage-environment-conflict', bindingId,
181
+ message: describeEnvironmentCollisions(collisions) };
182
+ }
183
+ for (const key of keys)
184
+ claimedKeys.add(key);
185
+ if (resolution.status === 'composed') {
186
+ environment = { ...environment, ...resolution.environment };
187
+ }
188
+ else {
189
+ wait ??= { status: 'waiting', reason: resolution.reason, bindingId, message: resolution.message };
190
+ }
191
+ }
192
+ return wait ?? { status: 'composed', environment };
193
+ };
194
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2JqZWN0c3RvcmFnZWVudmlyb25tZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcGxhdGZvcm0vb2JqZWN0c3RvcmFnZWVudmlyb25tZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQU1BLE9BQU8sRUFDTCxzQ0FBc0MsRUFDdEMsb0NBQW9DLEVBQ3BDLHdDQUF3QyxFQUN4Qyx1Q0FBdUMsRUFDdkMscUNBQXFDLEdBQ3RDLE1BQU0sb0JBQW9CLENBQUM7QUFTNUIsT0FBTyxFQUNMLHVDQUF1QyxFQUN2QyxxQ0FBcUMsRUFDckMseUNBQXlDLEVBQ3pDLHNDQUFzQyxFQUN0QyxvQ0FBb0MsR0FDckMsTUFBTSw2QkFBNkIsQ0FBQztBQUNyQyxPQUFPLEVBQUUsYUFBYSxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFFakY7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sbUNBQW1DLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUMvRCxHQUFHLG9DQUFvQztJQUN2QyxHQUFHLHFDQUFxQztDQUNoQyxDQUFDLENBQUM7QUF3Qlo7OztHQUdHO0FBQ0gsTUFBTSx5QkFBeUIsR0FBRyxLQUFLLEVBQ3JDLFVBQTRCLEVBQzVCLFdBQWtELEVBQ1YsRUFBRTtJQUMxQyxNQUFNLE9BQU8sR0FBRyxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUMvQyxJQUFJLE9BQU8sS0FBSyxJQUFJLElBQUksT0FBTyxDQUFDLFVBQVUsS0FBSyxlQUFlLEVBQUUsQ0FBQztRQUMvRCxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsK0JBQStCO1lBQ2pFLE9BQU8sRUFBRSxxRUFBcUUsRUFBRSxDQUFDO0lBQ3JGLENBQUM7SUFDRCxNQUFNLFdBQVcsR0FBRyxPQUFPLE9BQU8sQ0FBQyxFQUFFLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxXQUFX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
@@ -5,6 +5,7 @@ import type { IClusterNode, INodeAction, INodeMetricsSample, TNodeActionName } f
5
5
  import type { IDeployment } from '../data/deployment.js';
6
6
  import type { IIdentity } from '../data/user.js';
7
7
  import type { IPlatformObjectStorageEndpoint } from '../platform/objectstorage.js';
8
+ import type { IPlatformDatabaseEndpoint } from '../platform/database.js';
8
9
  import type { INodeCredentialMetadata, INodeCredentialMutationPreconditions, INodeCredentialMutationResult, TNodeCredentialPurpose } from '../data/nodecredential.js';
9
10
  import type { INodeRuntimeRetirement } from '../data/noderetirement.js';
10
11
  import type { INodeRuntimeBindingRead, INodeRuntimeBindingRequest } from '../data/noderuntimebinding.js';
@@ -246,6 +247,34 @@ export interface IReq_Any_Cloudly_SetClusterNodeCorestoreObjectStorageEndpoint e
246
247
  node: IClusterNode;
247
248
  };
248
249
  }
250
+ /**
251
+ * An administrator states where a workload on one node reaches the node's Corestore database:
252
+ * `IClusterNode.data.corestoreDatabase`.
253
+ *
254
+ * A request of its own, separate from `setClusterNodeCorestoreEndpoint` and
255
+ * `setClusterNodeCorestoreObjectStorageEndpoint`: each sets one fact and never touches the others.
256
+ *
257
+ * Cloudly validates it with `validatePlatformDatabaseEndpoint` before it writes and takes the
258
+ * node's own real-write fence inside the writing transaction, as for the other two endpoints.
259
+ * `database: null` clears it, which is the same fact as never having set one: no database
260
+ * credential can be published for a binding on this node. A changed endpoint changes the binding
261
+ * request of every database binding placed on the node, so each is granted and published again,
262
+ * addressed to the new endpoint. The endpoint says where, never with what — no credential travels
263
+ * in this request or in the node document.
264
+ */
265
+ export interface IReq_Any_Cloudly_SetClusterNodeCorestoreDatabaseEndpoint extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_SetClusterNodeCorestoreDatabaseEndpoint> {
266
+ method: 'setClusterNodeCorestoreDatabaseEndpoint';
267
+ request: {
268
+ identity: IIdentity;
269
+ nodeId: string;
270
+ /** The node's database endpoint, or `null` to state that it has none. */
271
+ database: IPlatformDatabaseEndpoint | null;
272
+ };
273
+ response: {
274
+ /** The node as it now stands, so a caller needs no second read to see what it set. */
275
+ node: IClusterNode;
276
+ };
277
+ }
249
278
  export interface IReq_Any_Cloudly_CreateNodeAction extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Any_Cloudly_CreateNodeAction> {
250
279
  method: 'createNodeAction';
251
280
  request: {
@@ -2,6 +2,7 @@ import * as plugins from './plugins.js';
2
2
  import type { IActiveSecretRecipientMetadata, ICorestoreDatabaseAllocationReference, IIdentityCredential, ISecretVersionReference } from './data/index.js';
3
3
  import type { ISecretEnvelopeAdmissionBinding } from './runtime.js';
4
4
  import type { IObjectStorageRetentionIntent, IObjectStorageRetentionEvidenceExpectation, IObjectStorageRetentionEvidence } from './platform/objectstorageretention.js';
5
+ import type { IPlatformDatabaseEndpoint } from './platform/database.js';
5
6
  export type TCorestoreCredentialCapability = 'database' | 'objectstorage';
6
7
  export declare const corestoreProviderConfigIds: Readonly<{
7
8
  readonly database: "cloudly-corestore-database";
@@ -93,6 +94,14 @@ interface ICorestoreCredentialBindingRequestBase {
93
94
  }
94
95
  export type TCorestoreCredentialBindingRequest = ICorestoreCredentialBindingRequestBase & {
95
96
  capability: 'database';
97
+ /**
98
+ * Where a workload reaches the database: the `corestoreDatabase` of the node the binding is
99
+ * placed on. Corestore addresses the binding's connection values to it, and it is part of
100
+ * the request digest, so a changed endpoint is a changed request that is granted and
101
+ * published again. A request without it names no address a workload can reach; whether one
102
+ * is admitted is the provider's decision, never a default this contract supplies.
103
+ */
104
+ endpoint?: IPlatformDatabaseEndpoint;
96
105
  } | ICorestoreCredentialBindingRequestBase & {
97
106
  capability: 'objectstorage';
98
107
  bucketName: string;
@@ -129,6 +138,33 @@ export interface ICorestoreObjectStorageCredentialPlaintextMaterial {
129
138
  }
130
139
  export type TCorestoreCredentialPlaintextMaterial = ICorestoreDatabaseCredentialPlaintextMaterial | ICorestoreObjectStorageCredentialPlaintextMaterial;
131
140
  export declare const validateCorestoreDatabaseCredentialMaterial: (materialArg: unknown) => string[];
141
+ /** What a database binding's connection values are made of, besides where the database is. */
142
+ export interface ICorestoreDatabaseCredentialIdentity {
143
+ databaseName: string;
144
+ username: string;
145
+ password: string;
146
+ }
147
+ /**
148
+ * The connection URI of one database binding, addressed to `endpointArg`:
149
+ * `mongodb://<username>:<password>@<host>:<port>/<databaseName>?authSource=<databaseName>&directConnection=true`,
150
+ * the username, password and database name URI-component encoded, an IPv6 host in brackets.
151
+ * This is the one form Corestore writes and Cloudly checks, so the two can never disagree.
152
+ */
153
+ export declare const createCorestoreDatabaseUri: (endpointArg: IPlatformDatabaseEndpoint, identityArg: ICorestoreDatabaseCredentialIdentity) => string;
154
+ /**
155
+ * The eleven connection values of one database binding, addressed to `endpointArg`: the three URI
156
+ * aliases from `createCorestoreDatabaseUri()`, `MONGODB_HOST` the endpoint host without brackets,
157
+ * `MONGODB_PORT` its port in decimal, and the database, username and password under both alias
158
+ * families. Throws the first reason the result would not be valid material; nothing is defaulted.
159
+ */
160
+ export declare const createCorestoreDatabaseCredentialValues: (endpointArg: IPlatformDatabaseEndpoint, identityArg: ICorestoreDatabaseCredentialIdentity) => TCorestoreDatabaseCredentialValues;
161
+ /**
162
+ * Validates database material and that it is addressed to `endpointArg`: every value is the one
163
+ * `createCorestoreDatabaseCredentialValues()` derives from the endpoint and the material's own
164
+ * database name, username and password. Material a provider addressed to any other host or port —
165
+ * its own public host above all — is refused by name.
166
+ */
167
+ export declare const validateCorestoreDatabaseCredentialMaterialEndpoint: (materialArg: unknown, endpointArg: IPlatformDatabaseEndpoint) => string[];
132
168
  export declare const validateCorestoreObjectStorageCredentialMaterial: (materialArg: unknown, retentionExpectationArg?: IObjectStorageRetentionEvidenceExpectation) => Promise<string[]>;
133
169
  export declare const validateCorestoreCredentialPlaintextMaterial: (materialArg: unknown, retentionExpectationArg?: IObjectStorageRetentionEvidenceExpectation) => Promise<string[]>;
134
170
  export interface ICorestoreCredentialSecretValue {