@serve.zone/interfaces 25.1.1 → 25.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/changelog.md +19 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/platform/index.d.ts +3 -1
  4. package/dist_ts/platform/index.js +4 -2
  5. package/dist_ts/platform/objectstorageretention.d.ts +63 -0
  6. package/dist_ts/platform/objectstorageretention.js +255 -0
  7. package/dist_ts/platform/types.d.ts +3 -0
  8. package/dist_ts/plugins.runtime.d.ts +2 -0
  9. package/dist_ts/plugins.runtime.js +4 -0
  10. package/dist_ts/requests/config.d.ts +3 -0
  11. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.d.ts +394 -0
  12. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.d.ts +35 -0
  13. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.js +315 -0
  14. package/dist_ts/runtime.cloudlylegacydeploymentsettlement.js +1378 -0
  15. package/dist_ts/runtime.corestore.d.ts +204 -0
  16. package/dist_ts/runtime.corestore.js +620 -0
  17. package/dist_ts/runtime.d.ts +2 -0
  18. package/dist_ts/runtime.js +3 -1
  19. package/dist_ts/runtime.workloadinit.d.ts +1 -1
  20. package/package.json +5 -1
  21. package/readme.md +54 -0
  22. package/ts/00_commitinfo_data.ts +1 -1
  23. package/ts/platform/index.ts +3 -0
  24. package/ts/platform/objectstorageretention.ts +372 -0
  25. package/ts/platform/types.ts +3 -0
  26. package/ts/plugins.runtime.ts +4 -0
  27. package/ts/requests/config.ts +3 -0
  28. package/ts/runtime.cloudlylegacydeploymentsettlement.golden.ts +421 -0
  29. package/ts/runtime.cloudlylegacydeploymentsettlement.ts +2884 -0
  30. package/ts/runtime.corestore.ts +909 -0
  31. package/ts/runtime.ts +2 -0
  32. package/ts/runtime.workloadinit.ts +1 -1
@@ -0,0 +1,372 @@
1
+ import { createCanonicalJsonSha256Hex } from '../private/canonicaljson.js';
2
+
3
+ export const objectStorageRetentionLimits = Object.freeze({
4
+ maximumPolicyIdLength: 96,
5
+ maximumRetentionDurationSeconds: 100 * 366 * 24 * 60 * 60,
6
+ });
7
+
8
+ export interface IObjectStorageRetentionIntentV1 {
9
+ mode: 'compliance';
10
+ policyId: string;
11
+ retentionDurationSeconds: number;
12
+ }
13
+
14
+ export interface IObjectStorageRetentionCapabilityEvidenceV1 {
15
+ version: 1;
16
+ supported: true;
17
+ backend: 'standalone' | 'clustered';
18
+ modes: ['compliance'];
19
+ atomicCreateOnly: true;
20
+ retainedMultipartSupported: false;
21
+ }
22
+
23
+ export interface IObjectStorageRetentionAuthorityV1 {
24
+ serviceId: string;
25
+ bindingId: string;
26
+ reconciliationGeneration: number;
27
+ bindingRequestDigest: string;
28
+ }
29
+
30
+ export interface IObjectStorageRetentionSentinelEvidenceV1
31
+ extends IObjectStorageRetentionIntentV1 {
32
+ version: 1;
33
+ createdAt: number;
34
+ retainUntil: number;
35
+ payloadSha256: string;
36
+ metadataSha256: string;
37
+ etag: string;
38
+ }
39
+
40
+ export interface IObjectStorageRetentionReceiptV1
41
+ extends IObjectStorageRetentionIntentV1 {
42
+ bucketName: string;
43
+ configuredAt: number;
44
+ sentinelKey: string;
45
+ sentinel: IObjectStorageRetentionSentinelEvidenceV1;
46
+ }
47
+
48
+ export interface IObjectStorageRetentionEvidenceV1 {
49
+ intent: IObjectStorageRetentionIntentV1;
50
+ intentSha256: string;
51
+ authority: IObjectStorageRetentionAuthorityV1;
52
+ capability: IObjectStorageRetentionCapabilityEvidenceV1;
53
+ receipt: IObjectStorageRetentionReceiptV1;
54
+ verifiedAt: number;
55
+ }
56
+
57
+ /** Value-free desired intent with optional exact provider evidence. */
58
+ export interface IObjectStorageRetentionBindingV1 {
59
+ schemaVersion: 1;
60
+ intent: IObjectStorageRetentionIntentV1;
61
+ evidence?: IObjectStorageRetentionEvidenceV1;
62
+ }
63
+
64
+ export interface IObjectStorageRetentionEvidenceExpectationV1
65
+ extends IObjectStorageRetentionAuthorityV1 {
66
+ intent: IObjectStorageRetentionIntentV1;
67
+ bucketName: string;
68
+ }
69
+
70
+ const policyIdPattern = /^[A-Za-z0-9._-]{1,96}$/;
71
+ const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/;
72
+ const sha256Pattern = /^[a-f0-9]{64}$/;
73
+ const etagPattern = /^[a-f0-9]{32}$/;
74
+ const sentinelKeyPattern = /^\.smartstorage-retention\/v1\/sentinel-[a-f0-9]{64}\.json$/;
75
+
76
+ const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => (
77
+ Boolean(valueArg)
78
+ && typeof valueArg === 'object'
79
+ && !Array.isArray(valueArg)
80
+ && (Object.getPrototypeOf(valueArg) === Object.prototype
81
+ || Object.getPrototypeOf(valueArg) === null)
82
+ );
83
+
84
+ const hasExactKeys = (
85
+ valueArg: Record<string, unknown>,
86
+ keysArg: readonly string[],
87
+ ): boolean => {
88
+ const actualKeys = Object.keys(valueArg).sort();
89
+ const expectedKeys = [...keysArg].sort();
90
+ return actualKeys.length === expectedKeys.length
91
+ && actualKeys.every((keyArg, indexArg) => keyArg === expectedKeys[indexArg]);
92
+ };
93
+
94
+ const isPositiveSafeInteger = (valueArg: unknown): valueArg is number => (
95
+ Number.isSafeInteger(valueArg) && (valueArg as number) > 0
96
+ );
97
+
98
+ export const validateObjectStorageRetentionIntent = (
99
+ intentArg: unknown,
100
+ pathArg = 'objectstorage retention intent',
101
+ ): string[] => {
102
+ if (!isRecord(intentArg)
103
+ || !hasExactKeys(intentArg, ['mode', 'policyId', 'retentionDurationSeconds'])) {
104
+ return [`${pathArg} must use its exact schema`];
105
+ }
106
+ const errors: string[] = [];
107
+ if (intentArg.mode !== 'compliance'
108
+ || typeof intentArg.policyId !== 'string'
109
+ || !policyIdPattern.test(intentArg.policyId)) {
110
+ errors.push(`${pathArg} must identify a canonical compliance policy`);
111
+ }
112
+ if (!isPositiveSafeInteger(intentArg.retentionDurationSeconds)
113
+ || (intentArg.retentionDurationSeconds as number)
114
+ > objectStorageRetentionLimits.maximumRetentionDurationSeconds) {
115
+ errors.push(`${pathArg} duration must be a positive bounded safe integer`);
116
+ }
117
+ return errors;
118
+ };
119
+
120
+ export const createObjectStorageRetentionRetainUntil = (
121
+ createdAtArg: number,
122
+ retentionDurationSecondsArg: number,
123
+ ): number => {
124
+ if (!isPositiveSafeInteger(createdAtArg)
125
+ || !isPositiveSafeInteger(retentionDurationSecondsArg)
126
+ || retentionDurationSecondsArg > objectStorageRetentionLimits.maximumRetentionDurationSeconds) {
127
+ throw new Error('objectstorage retention arithmetic requires positive bounded safe integers');
128
+ }
129
+ const retainUntil = createdAtArg + retentionDurationSecondsArg * 1000;
130
+ if (!Number.isSafeInteger(retainUntil) || retainUntil <= createdAtArg) {
131
+ throw new Error('objectstorage retention retainUntil exceeds safe integer bounds');
132
+ }
133
+ return retainUntil;
134
+ };
135
+
136
+ export const createObjectStorageRetentionIntentDigestInput = (
137
+ intentArg: IObjectStorageRetentionIntentV1,
138
+ ): string => {
139
+ const errors = validateObjectStorageRetentionIntent(intentArg);
140
+ if (errors.length > 0) throw new Error(errors[0]);
141
+ return JSON.stringify({
142
+ capability: 'objectstorage',
143
+ retention: {
144
+ mode: intentArg.mode,
145
+ policyId: intentArg.policyId,
146
+ retentionDurationSeconds: intentArg.retentionDurationSeconds,
147
+ },
148
+ version: 1,
149
+ });
150
+ };
151
+
152
+ export const computeObjectStorageRetentionIntentSha256 = async (
153
+ intentArg: IObjectStorageRetentionIntentV1,
154
+ ): Promise<string> => createCanonicalJsonSha256Hex(
155
+ createObjectStorageRetentionIntentDigestInput(intentArg),
156
+ (reasonArg) => { throw new Error(reasonArg); },
157
+ );
158
+
159
+ export const objectStorageRetentionIntentsEqual = (
160
+ leftArg: unknown,
161
+ rightArg: unknown,
162
+ ): boolean => validateObjectStorageRetentionIntent(leftArg).length === 0
163
+ && validateObjectStorageRetentionIntent(rightArg).length === 0
164
+ && (leftArg as IObjectStorageRetentionIntentV1).mode
165
+ === (rightArg as IObjectStorageRetentionIntentV1).mode
166
+ && (leftArg as IObjectStorageRetentionIntentV1).policyId
167
+ === (rightArg as IObjectStorageRetentionIntentV1).policyId
168
+ && (leftArg as IObjectStorageRetentionIntentV1).retentionDurationSeconds
169
+ === (rightArg as IObjectStorageRetentionIntentV1).retentionDurationSeconds;
170
+
171
+ const retentionIntentFieldsEqual = (
172
+ leftArg: unknown,
173
+ rightArg: unknown,
174
+ ): boolean => isRecord(leftArg)
175
+ && isRecord(rightArg)
176
+ && leftArg.mode === 'compliance'
177
+ && rightArg.mode === 'compliance'
178
+ && typeof leftArg.policyId === 'string'
179
+ && leftArg.policyId === rightArg.policyId
180
+ && isPositiveSafeInteger(leftArg.retentionDurationSeconds)
181
+ && leftArg.retentionDurationSeconds === rightArg.retentionDurationSeconds;
182
+
183
+ export const validateObjectStorageRetentionEvidence = async (
184
+ evidenceArg: unknown,
185
+ expectedArg: IObjectStorageRetentionEvidenceExpectationV1,
186
+ ): Promise<string[]> => {
187
+ if (!isRecord(evidenceArg)
188
+ || !hasExactKeys(evidenceArg, [
189
+ 'intent',
190
+ 'intentSha256',
191
+ 'authority',
192
+ 'capability',
193
+ 'receipt',
194
+ 'verifiedAt',
195
+ ])) {
196
+ return ['objectstorage retention evidence must use its exact schema'];
197
+ }
198
+ const errors = validateObjectStorageRetentionIntent(evidenceArg.intent);
199
+ const intent = evidenceArg.intent as IObjectStorageRetentionIntentV1;
200
+ if (!retentionIntentFieldsEqual(intent, expectedArg.intent)) {
201
+ errors.push('objectstorage retention evidence intent must equal the expected intent');
202
+ }
203
+ if (typeof evidenceArg.intentSha256 !== 'string'
204
+ || !sha256Pattern.test(evidenceArg.intentSha256)
205
+ || errors.length === 0
206
+ && evidenceArg.intentSha256 !== await computeObjectStorageRetentionIntentSha256(intent)) {
207
+ errors.push('objectstorage retention intent digest must exactly match the intent');
208
+ }
209
+
210
+ const authority = evidenceArg.authority;
211
+ if (!isRecord(authority)
212
+ || !hasExactKeys(authority, [
213
+ 'serviceId',
214
+ 'bindingId',
215
+ 'reconciliationGeneration',
216
+ 'bindingRequestDigest',
217
+ ])
218
+ || typeof authority.serviceId !== 'string'
219
+ || !identifierPattern.test(authority.serviceId)
220
+ || authority.serviceId !== expectedArg.serviceId
221
+ || typeof authority.bindingId !== 'string'
222
+ || !identifierPattern.test(authority.bindingId)
223
+ || authority.bindingId !== expectedArg.bindingId
224
+ || !isPositiveSafeInteger(authority.reconciliationGeneration)
225
+ || authority.reconciliationGeneration !== expectedArg.reconciliationGeneration
226
+ || typeof authority.bindingRequestDigest !== 'string'
227
+ || !sha256Pattern.test(authority.bindingRequestDigest)
228
+ || authority.bindingRequestDigest !== expectedArg.bindingRequestDigest) {
229
+ errors.push('objectstorage retention evidence authority must match the trusted binding fence');
230
+ }
231
+
232
+ const capability = evidenceArg.capability;
233
+ if (!isRecord(capability)
234
+ || !hasExactKeys(capability, [
235
+ 'version',
236
+ 'supported',
237
+ 'backend',
238
+ 'modes',
239
+ 'atomicCreateOnly',
240
+ 'retainedMultipartSupported',
241
+ ])
242
+ || capability.version !== 1
243
+ || capability.supported !== true
244
+ || (capability.backend !== 'standalone' && capability.backend !== 'clustered')
245
+ || !Array.isArray(capability.modes)
246
+ || capability.modes.length !== 1
247
+ || capability.modes[0] !== 'compliance'
248
+ || capability.atomicCreateOnly !== true
249
+ || capability.retainedMultipartSupported !== false) {
250
+ errors.push('objectstorage retention capability evidence must be exact');
251
+ }
252
+
253
+ const receipt = evidenceArg.receipt;
254
+ if (!isRecord(receipt)
255
+ || !hasExactKeys(receipt, [
256
+ 'mode',
257
+ 'policyId',
258
+ 'retentionDurationSeconds',
259
+ 'bucketName',
260
+ 'configuredAt',
261
+ 'sentinelKey',
262
+ 'sentinel',
263
+ ])) {
264
+ errors.push('objectstorage retention receipt must use its exact schema');
265
+ return errors;
266
+ }
267
+ if (!retentionIntentFieldsEqual(receipt, intent)) {
268
+ errors.push('objectstorage retention receipt intent must equal its evidence intent');
269
+ }
270
+ if (typeof receipt.bucketName !== 'string'
271
+ || receipt.bucketName.length === 0
272
+ || receipt.bucketName.length > 200
273
+ || receipt.bucketName !== expectedArg.bucketName) {
274
+ errors.push('objectstorage retention receipt bucket must exactly match');
275
+ }
276
+ if (!isPositiveSafeInteger(receipt.configuredAt)
277
+ || typeof receipt.sentinelKey !== 'string'
278
+ || !sentinelKeyPattern.test(receipt.sentinelKey)) {
279
+ errors.push('objectstorage retention receipt configuration evidence must be canonical');
280
+ }
281
+
282
+ const sentinel = receipt.sentinel;
283
+ if (!isRecord(sentinel)
284
+ || !hasExactKeys(sentinel, [
285
+ 'version',
286
+ 'mode',
287
+ 'policyId',
288
+ 'retentionDurationSeconds',
289
+ 'createdAt',
290
+ 'retainUntil',
291
+ 'payloadSha256',
292
+ 'metadataSha256',
293
+ 'etag',
294
+ ])) {
295
+ errors.push('objectstorage retention sentinel evidence must use its exact schema');
296
+ return errors;
297
+ }
298
+ if (sentinel.version !== 1 || !retentionIntentFieldsEqual(sentinel, intent)) {
299
+ errors.push('objectstorage retention sentinel intent must equal its evidence intent');
300
+ }
301
+ if (!isPositiveSafeInteger(sentinel.createdAt)
302
+ || !isPositiveSafeInteger(sentinel.retainUntil)
303
+ || !isPositiveSafeInteger(receipt.configuredAt)
304
+ || (sentinel.createdAt as number) < (receipt.configuredAt as number)) {
305
+ errors.push('objectstorage retention timestamps must be positive and ordered');
306
+ } else {
307
+ try {
308
+ if (sentinel.retainUntil !== createObjectStorageRetentionRetainUntil(
309
+ sentinel.createdAt,
310
+ intent.retentionDurationSeconds,
311
+ )) {
312
+ errors.push('objectstorage retention retainUntil must equal createdAt plus duration');
313
+ }
314
+ } catch {
315
+ errors.push('objectstorage retention retainUntil arithmetic must be safe');
316
+ }
317
+ }
318
+ if (typeof sentinel.payloadSha256 !== 'string' || !sha256Pattern.test(sentinel.payloadSha256)
319
+ || typeof sentinel.metadataSha256 !== 'string' || !sha256Pattern.test(sentinel.metadataSha256)
320
+ || typeof sentinel.etag !== 'string' || !etagPattern.test(sentinel.etag)) {
321
+ errors.push('objectstorage retention sentinel digests and ETag must be canonical');
322
+ }
323
+ if (!isPositiveSafeInteger(evidenceArg.verifiedAt)
324
+ || !isPositiveSafeInteger(sentinel.createdAt)
325
+ || (evidenceArg.verifiedAt as number) < (sentinel.createdAt as number)) {
326
+ errors.push('objectstorage retention verification timestamp must follow sentinel creation');
327
+ }
328
+ return errors;
329
+ };
330
+
331
+ export const validateObjectStorageRetentionBinding = async (
332
+ bindingArg: unknown,
333
+ expectedArg: IObjectStorageRetentionEvidenceExpectationV1,
334
+ ): Promise<string[]> => {
335
+ if (!isRecord(bindingArg)
336
+ || !Object.hasOwn(bindingArg, 'schemaVersion')
337
+ || !Object.hasOwn(bindingArg, 'intent')
338
+ || Object.keys(bindingArg).some((keyArg) => !['schemaVersion', 'intent', 'evidence'].includes(keyArg))) {
339
+ return ['objectstorage retention binding must use its exact schema'];
340
+ }
341
+ const errors = bindingArg.schemaVersion === 1
342
+ ? validateObjectStorageRetentionIntent(bindingArg.intent)
343
+ : ['objectstorage retention binding schemaVersion must be 1'];
344
+ if (!retentionIntentFieldsEqual(bindingArg.intent, expectedArg.intent)) {
345
+ errors.push('objectstorage retention binding intent must equal the trusted expected intent');
346
+ }
347
+ if (bindingArg.evidence !== undefined) {
348
+ errors.push(...await validateObjectStorageRetentionEvidence(
349
+ bindingArg.evidence,
350
+ expectedArg,
351
+ ));
352
+ }
353
+ return errors;
354
+ };
355
+
356
+ export const validatePlatformBindingObjectStorageRetention = async (
357
+ bindingArg: unknown,
358
+ expectedArg: IObjectStorageRetentionEvidenceExpectationV1,
359
+ ): Promise<string[]> => {
360
+ if (!isRecord(bindingArg)) {
361
+ return ['platform objectstorage retention binding must be an object'];
362
+ }
363
+ if (bindingArg.capability !== 'objectstorage'
364
+ || bindingArg.id !== expectedArg.bindingId
365
+ || bindingArg.serviceId !== expectedArg.serviceId) {
366
+ return ['platform objectstorage retention must match an objectstorage binding authority'];
367
+ }
368
+ if (bindingArg.objectstorageRetention === undefined) {
369
+ return ['platform objectstorage retention binding must contain retention intent'];
370
+ }
371
+ return validateObjectStorageRetentionBinding(bindingArg.objectstorageRetention, expectedArg);
372
+ };
@@ -1,4 +1,5 @@
1
1
  import type { TSecretManagementScope } from '../data/secret.js';
2
+ import type { IObjectStorageRetentionBindingV1 } from './objectstorageretention.js';
2
3
 
3
4
  export type TPlatformCapability =
4
5
  | 'email'
@@ -64,6 +65,8 @@ export interface IPlatformBinding {
64
65
  endpoints?: IPlatformServiceEndpoint[];
65
66
  /** Value-free reconciliation scope for service-owned credentials. */
66
67
  credentialManagementScope?: Extract<TSecretManagementScope, `platform:${string}`>;
68
+ /** Capability-specific value-free immutable-retention intent and evidence. */
69
+ objectstorageRetention?: IObjectStorageRetentionBindingV1;
67
70
  createdAt?: number;
68
71
  updatedAt?: number;
69
72
  }
@@ -0,0 +1,4 @@
1
+ // node native scope for the Node-only /runtime export
2
+ import * as crypto from 'node:crypto';
3
+
4
+ export { crypto };
@@ -4,6 +4,7 @@ import * as serverInterfaces from '../data/server.js';
4
4
  import * as userInterfaces from '../data/user.js';
5
5
  import type { IService } from '../data/service.js';
6
6
  import type { IPlatformBinding, IPlatformProviderConfig } from '../platform/types.js';
7
+ import type { ICorestoreCredentialPublicationGrantV1 } from '../runtime.corestore.js';
7
8
 
8
9
  export interface IRequest_Any_Cloudly_GetServerConfig
9
10
  extends plugins.typedrequestInterfaces.implementsTR<
@@ -34,6 +35,7 @@ extends plugins.typedrequestInterfaces.implementsTR<
34
35
  services: IService[];
35
36
  platformProviderConfigs?: IPlatformProviderConfig[];
36
37
  platformBindings?: IPlatformBinding[];
38
+ corestoreCredentialPublicationGrants?: ICorestoreCredentialPublicationGrantV1[];
37
39
  };
38
40
  }
39
41
 
@@ -48,6 +50,7 @@ extends plugins.typedrequestInterfaces.implementsTR<
48
50
  services: IService[];
49
51
  platformProviderConfigs?: IPlatformProviderConfig[];
50
52
  platformBindings?: IPlatformBinding[];
53
+ corestoreCredentialPublicationGrants?: ICorestoreCredentialPublicationGrantV1[];
51
54
  };
52
55
  response: {};
53
56
  }