@serve.zone/interfaces 32.24.0 → 32.26.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.
@@ -18,4 +18,139 @@ export const validatePlatformObjectStorageBucketName = (bucketNameArg, pathArg =
18
18
  }
19
19
  return [];
20
20
  };
21
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2JqZWN0c3RvcmFnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3BsYXRmb3JtL29iamVjdHN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBT0EsTUFBTSxDQUFDLE1BQU0scUNBQXFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUNqRSxZQUFZLEVBQUUsQ0FBQztJQUNmLFlBQVksRUFBRSxFQUFFO0NBQ2pCLENBQUMsQ0FBQztBQUVILHNGQUFzRjtBQUN0RixNQUFNLENBQUMsTUFBTSx1Q0FBdUMsR0FBRyxDQUNyRCxhQUFzQixFQUN0QixPQUFPLEdBQUcsMEJBQTBCLEVBQzFCLEVBQUU7SUFDWixJQUFJLE9BQU8sYUFBYSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ3RDLE9BQU8sQ0FBQyxHQUFHLE9BQU8scUNBQXFDLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBQ0QsTUFBTSxTQUFTLEdBQUcsYUFBYSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUMzQyxNQUFNLGFBQWEsR0FBRyxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsSUFBSSxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUMzRSxVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxHQUFHLENBQ25ELENBQUMsQ0FBQztJQUNILElBQUksYUFBYSxDQUFDLE1BQU0sR0FBRyxxQ0FBcUMsQ0FBQyxZQUFZO1dBQ3hFLGFBQWEsQ0FBQyxNQUFNLEdBQUcscUNBQXFDLENBQUMsWUFBWTtXQUN6RSxDQUFDLCtCQUErQixDQUFDLElBQUksQ0FBQyxhQUFhLENBQUM7V0FDcEQsYUFBYSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7V0FDNUIsYUFBYSxFQUFFLENBQUM7UUFDbkIsT0FBTyxDQUFDLEdBQUcsT0FBTyxxQ0FBcUMsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFDRCxPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUMsQ0FBQyJ9
21
+ export const platformObjectStorageEndpointLimits = Object.freeze({
22
+ maximumEndpointLength: 2048,
23
+ maximumRegionLength: 64,
24
+ });
25
+ const platformObjectStorageRegionPattern = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
26
+ const isPlainObject = (valueArg) => (Boolean(valueArg)
27
+ && typeof valueArg === 'object'
28
+ && !Array.isArray(valueArg)
29
+ && (Object.getPrototypeOf(valueArg) === Object.prototype
30
+ || Object.getPrototypeOf(valueArg) === null));
31
+ /** Validates an object-storage endpoint and names every reason it is refused. */
32
+ export const validatePlatformObjectStorageEndpoint = (valueArg, pathArg = 'objectstorage endpoint') => {
33
+ if (!isPlainObject(valueArg)
34
+ || Object.keys(valueArg).length !== 2
35
+ || !Object.hasOwn(valueArg, 'endpoint')
36
+ || !Object.hasOwn(valueArg, 'region')) {
37
+ return [`${pathArg} must use its exact schema`];
38
+ }
39
+ const errors = [];
40
+ const endpoint = valueArg.endpoint;
41
+ if (typeof endpoint !== 'string'
42
+ || endpoint.length === 0
43
+ || endpoint.length > platformObjectStorageEndpointLimits.maximumEndpointLength) {
44
+ errors.push(`${pathArg} endpoint must be a string of 1 to `
45
+ + `${platformObjectStorageEndpointLimits.maximumEndpointLength} characters`);
46
+ }
47
+ else {
48
+ let parsedEndpoint;
49
+ try {
50
+ parsedEndpoint = new URL(endpoint);
51
+ }
52
+ catch {
53
+ errors.push(`${pathArg} endpoint must be a parsable URL`);
54
+ }
55
+ if (parsedEndpoint) {
56
+ if (parsedEndpoint.protocol !== 'http:' && parsedEndpoint.protocol !== 'https:') {
57
+ errors.push(`${pathArg} endpoint must use the http or https scheme`);
58
+ }
59
+ if (parsedEndpoint.username || parsedEndpoint.password) {
60
+ errors.push(`${pathArg} endpoint must not carry userinfo`);
61
+ }
62
+ if (parsedEndpoint.origin !== endpoint) {
63
+ errors.push(`${pathArg} endpoint must be a canonical origin without a path, query, `
64
+ + 'fragment, trailing slash or default port');
65
+ }
66
+ }
67
+ }
68
+ if (typeof valueArg.region !== 'string'
69
+ || !platformObjectStorageRegionPattern.test(valueArg.region)) {
70
+ errors.push(`${pathArg} region must be 1 to `
71
+ + `${platformObjectStorageEndpointLimits.maximumRegionLength} lowercase letters, digits `
72
+ + 'and inner hyphens');
73
+ }
74
+ return errors;
75
+ };
76
+ /**
77
+ * The non-secret environment names that tell a workload where its bucket is.
78
+ *
79
+ * These are the names Corestore has always handed a workload for a bucket, so an application that
80
+ * read them keeps working unchanged. The six credential aliases (`S3_ACCESS_KEY`,
81
+ * `S3_ACCESS_KEY_ID`, `S3_SECRET_KEY`, `S3_SECRET_ACCESS_KEY`, `AWS_ACCESS_KEY_ID`,
82
+ * `AWS_SECRET_ACCESS_KEY`) are not among them: they are Secrets and reach the workload only through
83
+ * the binding's sealed Secret set.
84
+ *
85
+ * - `S3_ENDPOINT`, `AWS_ENDPOINT_URL`: the endpoint origin, identical.
86
+ * - `S3_ENDPOINT_HOST`: the origin's host, as the URL states it (an IPv6 literal keeps its brackets).
87
+ * - `S3_PORT`: the origin's port in decimal, `80` or `443` when the origin states none.
88
+ * - `S3_USE_SSL`: `true` for an `https:` origin, `false` for `http:`.
89
+ * - `S3_REGION`, `AWS_REGION`: the signing region, identical.
90
+ * - `S3_BUCKET`: the binding's bucket, `IPlatformBinding.objectstorageBucketName`.
91
+ * - `AWS_S3_FORCE_PATH_STYLE`: always `true` — a node-local origin has no wildcard DNS and a bucket
92
+ * name may contain dots, so path-style addressing is the only one that reaches the bucket.
93
+ */
94
+ export const objectStorageEndpointEnvironmentKeys = Object.freeze([
95
+ 'S3_ENDPOINT',
96
+ 'S3_ENDPOINT_HOST',
97
+ 'S3_PORT',
98
+ 'S3_REGION',
99
+ 'S3_BUCKET',
100
+ 'S3_USE_SSL',
101
+ 'AWS_ENDPOINT_URL',
102
+ 'AWS_REGION',
103
+ 'AWS_S3_FORCE_PATH_STYLE',
104
+ ]);
105
+ /**
106
+ * The endpoint environment for one bucket.
107
+ *
108
+ * Throws the first reason the endpoint or the bucket name is refused; nothing is defaulted.
109
+ */
110
+ export const createObjectStorageEndpointEnvironment = (endpointArg, bucketNameArg) => {
111
+ const errors = [
112
+ ...validatePlatformObjectStorageEndpoint(endpointArg),
113
+ ...validatePlatformObjectStorageBucketName(bucketNameArg),
114
+ ];
115
+ if (errors.length > 0)
116
+ throw new Error(errors[0]);
117
+ const origin = new URL(endpointArg.endpoint);
118
+ const useSsl = origin.protocol === 'https:';
119
+ return {
120
+ S3_ENDPOINT: endpointArg.endpoint,
121
+ S3_ENDPOINT_HOST: origin.hostname,
122
+ S3_PORT: origin.port || (useSsl ? '443' : '80'),
123
+ S3_REGION: endpointArg.region,
124
+ S3_BUCKET: bucketNameArg,
125
+ S3_USE_SSL: useSsl ? 'true' : 'false',
126
+ AWS_ENDPOINT_URL: endpointArg.endpoint,
127
+ AWS_REGION: endpointArg.region,
128
+ AWS_S3_FORCE_PATH_STYLE: 'true',
129
+ };
130
+ };
131
+ /**
132
+ * Validates an endpoint environment: exactly its nine keys, each value the one
133
+ * `createObjectStorageEndpointEnvironment()` derives from the same endpoint and bucket.
134
+ */
135
+ export const validateObjectStorageEndpointEnvironment = (environmentArg, pathArg = 'objectstorage endpoint environment') => {
136
+ if (!isPlainObject(environmentArg)
137
+ || Object.keys(environmentArg).length !== objectStorageEndpointEnvironmentKeys.length
138
+ || objectStorageEndpointEnvironmentKeys.some((keyArg) => (!Object.hasOwn(environmentArg, keyArg) || typeof environmentArg[keyArg] !== 'string'))) {
139
+ return [`${pathArg} must contain exactly its string keys`];
140
+ }
141
+ const endpoint = { endpoint: environmentArg.S3_ENDPOINT, region: environmentArg.S3_REGION };
142
+ const errors = [
143
+ ...validatePlatformObjectStorageEndpoint(endpoint, pathArg),
144
+ ...validatePlatformObjectStorageBucketName(environmentArg.S3_BUCKET, `${pathArg} S3_BUCKET`),
145
+ ];
146
+ if (errors.length > 0)
147
+ return errors;
148
+ const expected = createObjectStorageEndpointEnvironment(endpoint, environmentArg.S3_BUCKET);
149
+ for (const key of objectStorageEndpointEnvironmentKeys) {
150
+ if (environmentArg[key] !== expected[key]) {
151
+ errors.push(`${pathArg} ${key} must equal the value derived from its endpoint`);
152
+ }
153
+ }
154
+ return errors;
155
+ };
156
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2JqZWN0c3RvcmFnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3BsYXRmb3JtL29iamVjdHN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBT0EsTUFBTSxDQUFDLE1BQU0scUNBQXFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUNqRSxZQUFZLEVBQUUsQ0FBQztJQUNmLFlBQVksRUFBRSxFQUFFO0NBQ2pCLENBQUMsQ0FBQztBQUVILHNGQUFzRjtBQUN0RixNQUFNLENBQUMsTUFBTSx1Q0FBdUMsR0FBRyxDQUNyRCxhQUFzQixFQUN0QixPQUFPLEdBQUcsMEJBQTBCLEVBQzFCLEVBQUU7SUFDWixJQUFJLE9BQU8sYUFBYSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ3RDLE9BQU8sQ0FBQyxHQUFHLE9BQU8scUNBQXFDLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBQ0QsTUFBTSxTQUFTLEdBQUcsYUFBYSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUMzQyxNQUFNLGFBQWEsR0FBRyxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsSUFBSSxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUMzRSxVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxHQUFHLENBQ25ELENBQUMsQ0FBQztJQUNILElBQUksYUFBYSxDQUFDLE1BQU0sR0FBRyxxQ0FBcUMsQ0FBQyxZQUFZO1dBQ3hFLGFBQWEsQ0FBQyxNQUFNLEdBQUcscUNBQXFDLENBQUMsWUFBWTtXQUN6RSxDQUFDLCtCQUErQixDQUFDLElBQUksQ0FBQyxhQUFhLENBQUM7V0FDcEQsYUFBYSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7V0FDNUIsYUFBYSxFQUFFLENBQUM7UUFDbkIsT0FBTyxDQUFDLEdBQUcsT0FBTyxxQ0FBcUMsQ0FBQyxDQUFDO0lBQzNELENB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
@@ -0,0 +1,108 @@
1
+ import type { IPlatformBinding } from './types.js';
2
+ import type { IPlatformObjectStorageEndpoint, TObjectStorageEndpointEnvironment, TObjectStorageEndpointEnvironmentKey } from './objectstorage.js';
3
+ import type { TServiceRuntimeRefusal, TServiceRuntimeWaitingReason } from '../data/serviceruntime.js';
4
+ import type { TObjectStorageRetentionEnvironment, TObjectStorageRetentionEnvironmentKey } from './objectstorageretention.js';
5
+ /**
6
+ * Every non-secret environment name an object-storage binding can give a workload: the nine
7
+ * endpoint names first, then the eleven retention names.
8
+ */
9
+ export declare const objectStorageBindingEnvironmentKeys: readonly ["S3_ENDPOINT", "S3_ENDPOINT_HOST", "S3_PORT", "S3_REGION", "S3_BUCKET", "S3_USE_SSL", "AWS_ENDPOINT_URL", "AWS_REGION", "AWS_S3_FORCE_PATH_STYLE", "S3_RETENTION_MODE", "S3_RETENTION_POLICY_ID", "S3_RETENTION_DURATION_SECONDS", "S3_RETENTION_INTENT_SHA256", "S3_RETENTION_SENTINEL_KEY", "S3_RETENTION_CONFIGURED_AT", "S3_RETENTION_SENTINEL_CREATED_AT", "S3_RETENTION_SENTINEL_RETAIN_UNTIL", "S3_RETENTION_SENTINEL_PAYLOAD_SHA256", "S3_RETENTION_SENTINEL_METADATA_SHA256", "S3_RETENTION_SENTINEL_ETAG"];
10
+ export type TObjectStorageBindingEnvironmentKey = TObjectStorageEndpointEnvironmentKey | TObjectStorageRetentionEnvironmentKey;
11
+ /**
12
+ * The non-secret environment of one object-storage binding: always the endpoint names, and the
13
+ * retention names exactly when the binding declares compliance retention. Never a credential.
14
+ */
15
+ export type TObjectStorageBindingEnvironment = TObjectStorageEndpointEnvironment | (TObjectStorageEndpointEnvironment & TObjectStorageRetentionEnvironment);
16
+ /**
17
+ * The environment Cloudly composes into a workload's runtime configuration for one binding.
18
+ *
19
+ * `bindingArg` is the Cloudly binding document and `endpointArg` the
20
+ * `IClusterNode.data.corestoreObjectStorage` of the node the binding is placed on. Refused by
21
+ * name, never guessed:
22
+ *
23
+ * - a binding that is not `objectstorage` or carries no canonical `objectstorageBucketName`;
24
+ * - an endpoint that `validatePlatformObjectStorageEndpoint()` refuses;
25
+ * - a binding that declares `objectstorageRetention` without `evidence`. Its bucket's retention is
26
+ * not proven yet, so the workload is not given an environment that would let it run as if its
27
+ * bucket were ordinary storage; the caller waits for the evidence.
28
+ * - evidence that does not prove the binding's own service, binding id, bucket and intent.
29
+ *
30
+ * A binding without `objectstorageRetention` gets the endpoint names alone: it promised no
31
+ * retention, so there is nothing to state.
32
+ *
33
+ * The evidence's reconciliation generation and binding-request digest are read from its own
34
+ * authority: they are what Cloudly checked when it admitted the evidence, and the binding document
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.
39
+ */
40
+ export declare const createObjectStorageBindingEnvironment: (bindingArg: IPlatformBinding, endpointArg: IPlatformObjectStorageEndpoint) => Promise<TObjectStorageBindingEnvironment>;
41
+ /**
42
+ * Validates one binding's environment: the nine endpoint names, the eleven retention names all or
43
+ * none, and nothing else — so a credential alias, or any other name, is refused.
44
+ */
45
+ export declare const validateObjectStorageBindingEnvironment: (environmentArg: unknown, pathArg?: string) => Promise<string[]>;
46
+ /**
47
+ * A workload environment with one binding's environment added.
48
+ *
49
+ * Refuses, naming every key, when the environment already holds one of the binding's names: a
50
+ * service that sets `S3_ENDPOINT` itself, or a second object-storage binding on the same service,
51
+ * would otherwise have one value silently replace the other. The inputs are not changed.
52
+ */
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>;
@@ -0,0 +1,194 @@
1
+ import { createObjectStorageEndpointEnvironment, objectStorageEndpointEnvironmentKeys, validateObjectStorageEndpointEnvironment, validatePlatformObjectStorageBucketName, validatePlatformObjectStorageEndpoint, } from './objectstorage.js';
2
+ import { createObjectStorageRetentionEnvironment, objectStorageRetentionEnvironmentKeys, validateObjectStorageRetentionEnvironment, validateObjectStorageRetentionEvidence, validateObjectStorageRetentionIntent, } from './objectstorageretention.js';
3
+ import { isPlainRecord, readDataProperties } from '../private/dataproperties.js';
4
+ /**
5
+ * Every non-secret environment name an object-storage binding can give a workload: the nine
6
+ * endpoint names first, then the eleven retention names.
7
+ */
8
+ export const objectStorageBindingEnvironmentKeys = Object.freeze([
9
+ ...objectStorageEndpointEnvironmentKeys,
10
+ ...objectStorageRetentionEnvironmentKeys,
11
+ ]);
12
+ /**
13
+ * The one place a binding's environment is decided. `createObjectStorageBindingEnvironment()`
14
+ * throws its message, `composeObjectStorageServiceEnvironment()` reports its reason.
15
+ */
16
+ const resolveBindingEnvironment = async (bindingArg, endpointArg) => {
17
+ const binding = readDataProperties(bindingArg);
18
+ if (binding === null || binding.capability !== 'objectstorage') {
19
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
20
+ message: 'objectstorage binding environment requires an objectstorage binding' };
21
+ }
22
+ const bindingName = typeof binding.id === 'string' ? binding.id : '(unnamed)';
23
+ if (validatePlatformObjectStorageBucketName(binding.objectstorageBucketName).length > 0) {
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] };
36
+ }
37
+ const bucketName = binding.objectstorageBucketName;
38
+ const endpointEnvironment = createObjectStorageEndpointEnvironment(endpointArg, bucketName);
39
+ const retention = binding.objectstorageRetention;
40
+ if (retention === undefined) {
41
+ return { status: 'composed', environment: endpointEnvironment };
42
+ }
43
+ const retentionBinding = readDataProperties(retention);
44
+ if (retentionBinding === null
45
+ || validateObjectStorageRetentionIntent(retentionBinding.intent).length > 0) {
46
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
47
+ message: `objectstorage binding ${bindingName} must declare canonical compliance-retention `
48
+ + 'intent' };
49
+ }
50
+ const evidence = retentionBinding.evidence;
51
+ if (evidence === undefined) {
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' };
55
+ }
56
+ const authority = isPlainRecord(evidence) ? readDataProperties(evidence.authority) : null;
57
+ if (authority === null) {
58
+ return { status: 'refused', reason: 'objectstorage-binding-invalid',
59
+ message: 'objectstorage retention evidence must use its exact schema' };
60
+ }
61
+ const expectation = {
62
+ serviceId: binding.serviceId,
63
+ bindingId: binding.id,
64
+ reconciliationGeneration: authority.reconciliationGeneration,
65
+ bindingRequestDigest: authority.bindingRequestDigest,
66
+ bucketName,
67
+ intent: retentionBinding.intent,
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;
105
+ };
106
+ /**
107
+ * Validates one binding's environment: the nine endpoint names, the eleven retention names all or
108
+ * none, and nothing else — so a credential alias, or any other name, is refused.
109
+ */
110
+ export const validateObjectStorageBindingEnvironment = async (environmentArg, pathArg = 'objectstorage binding environment') => {
111
+ const environment = readDataProperties(environmentArg);
112
+ if (environment === null) {
113
+ return [`${pathArg} must be a plain record of data properties`];
114
+ }
115
+ const allowedKeys = objectStorageBindingEnvironmentKeys;
116
+ const foreignKeys = Object.keys(environment).filter((keyArg) => !allowedKeys.includes(keyArg));
117
+ if (foreignKeys.length > 0) {
118
+ return [`${pathArg} must not contain ${foreignKeys.sort().join(', ')}`];
119
+ }
120
+ const endpointEnvironment = {};
121
+ for (const key of objectStorageEndpointEnvironmentKeys) {
122
+ if (Object.hasOwn(environment, key))
123
+ endpointEnvironment[key] = environment[key];
124
+ }
125
+ const errors = validateObjectStorageEndpointEnvironment(endpointEnvironment, pathArg);
126
+ const retentionKeys = objectStorageRetentionEnvironmentKeys.filter((keyArg) => (Object.hasOwn(environment, keyArg)));
127
+ if (retentionKeys.length > 0) {
128
+ const retentionEnvironment = {};
129
+ for (const key of retentionKeys)
130
+ retentionEnvironment[key] = environment[key];
131
+ errors.push(...await validateObjectStorageRetentionEnvironment(retentionEnvironment, pathArg));
132
+ }
133
+ return errors;
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');
138
+ /**
139
+ * A workload environment with one binding's environment added.
140
+ *
141
+ * Refuses, naming every key, when the environment already holds one of the binding's names: a
142
+ * service that sets `S3_ENDPOINT` itself, or a second object-storage binding on the same service,
143
+ * would otherwise have one value silently replace the other. The inputs are not changed.
144
+ */
145
+ export const mergeObjectStorageBindingEnvironment = (environmentArg, bindingEnvironmentArg) => {
146
+ const collisions = findEnvironmentCollisions(new Set(Object.keys(environmentArg)), Object.keys(bindingEnvironmentArg));
147
+ if (collisions.length > 0)
148
+ throw new Error(describeEnvironmentCollisions(collisions));
149
+ return { ...environmentArg, ...bindingEnvironmentArg };
150
+ };
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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
@@ -65,3 +65,43 @@ export declare const objectStorageRetentionIntentsEqual: (leftArg: unknown, righ
65
65
  export declare const validateObjectStorageRetentionEvidence: (evidenceArg: unknown, expectedArg: IObjectStorageRetentionEvidenceExpectation) => Promise<string[]>;
66
66
  export declare const validateObjectStorageRetentionBinding: (bindingArg: unknown, expectedArg: IObjectStorageRetentionEvidenceExpectation) => Promise<string[]>;
67
67
  export declare const validatePlatformBindingObjectStorageRetention: (bindingArg: unknown, expectedArg: IObjectStorageRetentionEvidenceExpectation) => Promise<string[]>;
68
+ /**
69
+ * The non-secret environment names that tell a workload which retention its bucket carries.
70
+ *
71
+ * The names are the ones the cluster relay handed workloads on the Docker path, so an application
72
+ * that read them keeps working unchanged. Every value is a projection of verified
73
+ * `IObjectStorageRetentionEvidence`; none is a credential.
74
+ *
75
+ * - `S3_RETENTION_MODE`: `intent.mode`, always `compliance`.
76
+ * - `S3_RETENTION_POLICY_ID`: `intent.policyId`.
77
+ * - `S3_RETENTION_DURATION_SECONDS`: `intent.retentionDurationSeconds` in decimal.
78
+ * - `S3_RETENTION_INTENT_SHA256`: `intentSha256`.
79
+ * - `S3_RETENTION_SENTINEL_KEY`: `receipt.sentinelKey`.
80
+ * - `S3_RETENTION_CONFIGURED_AT`: `receipt.configuredAt`, Unix milliseconds in decimal.
81
+ * - `S3_RETENTION_SENTINEL_CREATED_AT`: `receipt.sentinel.createdAt`, Unix milliseconds in decimal.
82
+ * - `S3_RETENTION_SENTINEL_RETAIN_UNTIL`: `receipt.sentinel.retainUntil`, Unix milliseconds in
83
+ * decimal.
84
+ * - `S3_RETENTION_SENTINEL_PAYLOAD_SHA256`: `receipt.sentinel.payloadSha256`.
85
+ * - `S3_RETENTION_SENTINEL_METADATA_SHA256`: `receipt.sentinel.metadataSha256`.
86
+ * - `S3_RETENTION_SENTINEL_ETAG`: `receipt.sentinel.etag`.
87
+ */
88
+ export declare const objectStorageRetentionEnvironmentKeys: readonly ["S3_RETENTION_MODE", "S3_RETENTION_POLICY_ID", "S3_RETENTION_DURATION_SECONDS", "S3_RETENTION_INTENT_SHA256", "S3_RETENTION_SENTINEL_KEY", "S3_RETENTION_CONFIGURED_AT", "S3_RETENTION_SENTINEL_CREATED_AT", "S3_RETENTION_SENTINEL_RETAIN_UNTIL", "S3_RETENTION_SENTINEL_PAYLOAD_SHA256", "S3_RETENTION_SENTINEL_METADATA_SHA256", "S3_RETENTION_SENTINEL_ETAG"];
89
+ export type TObjectStorageRetentionEnvironmentKey = typeof objectStorageRetentionEnvironmentKeys[number];
90
+ export type TObjectStorageRetentionEnvironment = {
91
+ [TKey in TObjectStorageRetentionEnvironmentKey]: string;
92
+ };
93
+ /**
94
+ * The retention environment of evidence that proves `expectedArg`.
95
+ *
96
+ * The evidence is validated against the expectation first, with the same validator every other
97
+ * consumer of retention evidence uses; the first reason it is refused is thrown. A workload is
98
+ * never told about a retention nobody proved.
99
+ */
100
+ export declare const createObjectStorageRetentionEnvironment: (evidenceArg: IObjectStorageRetentionEvidence, expectedArg: IObjectStorageRetentionEvidenceExpectation) => Promise<TObjectStorageRetentionEnvironment>;
101
+ /**
102
+ * Validates a retention environment on its own: exactly its eleven keys, canonical values, an
103
+ * intent digest that matches the stated intent, and sentinel timestamps that agree with the
104
+ * duration. It cannot prove the authority or the bucket the evidence was bound to — the
105
+ * environment does not carry them — so it judges the shape a workload reads, not the proof.
106
+ */
107
+ export declare const validateObjectStorageRetentionEnvironment: (environmentArg: unknown, pathArg?: string) => Promise<string[]>;