@serve.zone/interfaces 25.1.0 → 25.2.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.
@@ -0,0 +1,909 @@
1
+ import * as plugins from './plugins.js';
2
+ import type {
3
+ IActiveSecretRecipientMetadata,
4
+ IIdentityCredential,
5
+ ISecretVersionReference,
6
+ } from './data/index.js';
7
+ import { isSha256Digest } from './data/immutableimage.js';
8
+ import {
9
+ validateSecretRecipientMetadata,
10
+ verifySecretEnvelopeContext,
11
+ } from './data/secret.js';
12
+ import type { ISecretEnvelopeAdmissionBindingV1 } from './runtime.js';
13
+ import type {
14
+ IObjectStorageRetentionEvidenceExpectationV1,
15
+ IObjectStorageRetentionEvidenceV1,
16
+ } from './platform/objectstorageretention.js';
17
+ import { validateObjectStorageRetentionEvidence } from './platform/objectstorageretention.js';
18
+
19
+ export type TCorestoreCredentialCapability = 'database' | 'objectstorage';
20
+
21
+ export const corestoreProviderConfigIds = Object.freeze({
22
+ database: 'cloudly-corestore-database',
23
+ objectstorage: 'cloudly-corestore-objectstorage',
24
+ } as const);
25
+ export const corestoreControlUrl = 'http://corestore:3000' as const;
26
+ export const corestoreCredentialManagementScope = 'platform:cloudly-corestore' as const;
27
+ export const corestoreControlCredentialKey = 'CORESTORE_API_TOKEN' as const;
28
+ export const corestoreCredentialEnvironment = 'production' as const;
29
+
30
+ export const corestoreCredentialRuntimeLimits = Object.freeze({
31
+ maximumIdentifierLength: 200,
32
+ maximumPublicationGrants: 256,
33
+ maximumGrantLifetimeMs: 24 * 60 * 60 * 1000,
34
+ maximumCredentialStringBytes: 8 * 1024,
35
+ maximumSealedMaterialBytes: 64 * 1024,
36
+ minimumControlTokenBytes: 32,
37
+ maximumControlTokenBytes: 4096,
38
+ maximumSealedControlCredentialBytes: 12 * 1024,
39
+ });
40
+
41
+ export const getCorestoreProviderConfigId = (
42
+ capabilityArg: TCorestoreCredentialCapability,
43
+ ): typeof corestoreProviderConfigIds[TCorestoreCredentialCapability] => {
44
+ if (capabilityArg !== 'database' && capabilityArg !== 'objectstorage') {
45
+ throw new Error('corestore capability must be database or objectstorage');
46
+ }
47
+ return corestoreProviderConfigIds[capabilityArg];
48
+ };
49
+
50
+ export interface ICorestoreControlCredentialPlaintextV1 {
51
+ schemaVersion: 1;
52
+ key: typeof corestoreControlCredentialKey;
53
+ environment: typeof corestoreCredentialEnvironment;
54
+ value: string;
55
+ }
56
+
57
+ export interface ICorestoreControlCredentialMaterialV1 {
58
+ schemaVersion: 1;
59
+ requestId: string;
60
+ clusterId: string;
61
+ capability: TCorestoreCredentialCapability;
62
+ providerConfigId: typeof corestoreProviderConfigIds[TCorestoreCredentialCapability];
63
+ controlUrl: typeof corestoreControlUrl;
64
+ managementScope: typeof corestoreCredentialManagementScope;
65
+ key: typeof corestoreControlCredentialKey;
66
+ environment: typeof corestoreCredentialEnvironment;
67
+ recipientKeyId: string;
68
+ recipientGeneration: number;
69
+ envelope: plugins.smartcrypto.IX25519EnvelopeV1;
70
+ }
71
+
72
+ export interface IReq_GetCorestoreControlCredentialMaterial
73
+ extends plugins.typedrequestInterfaces.implementsTR<
74
+ plugins.typedrequestInterfaces.ITypedRequest,
75
+ IReq_GetCorestoreControlCredentialMaterial
76
+ > {
77
+ method: 'getCorestoreControlCredentialMaterial';
78
+ request: {
79
+ identity: IIdentityCredential;
80
+ requestId: string;
81
+ capability: TCorestoreCredentialCapability;
82
+ expectedRecipientKeyId: string;
83
+ expectedRecipientGeneration: number;
84
+ };
85
+ response: {
86
+ material: ICorestoreControlCredentialMaterialV1;
87
+ };
88
+ }
89
+
90
+ export interface ICorestoreControlCredentialMaterialExpectationV1 {
91
+ requestId: string;
92
+ clusterId: string;
93
+ capability: TCorestoreCredentialCapability;
94
+ expectedRecipientKeyId: string;
95
+ expectedRecipientGeneration: number;
96
+ }
97
+
98
+ export interface ICorestoreControlCredentialMaterialContextInputV1 {
99
+ requestId: string;
100
+ clusterId: string;
101
+ capability: TCorestoreCredentialCapability;
102
+ recipientKeyId: string;
103
+ recipientGeneration: number;
104
+ }
105
+
106
+ const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/;
107
+ const bareSha256Pattern = /^[a-f0-9]{64}$/;
108
+ const environmentKeyPattern = /^[A-Z_][A-Z0-9_]{0,127}$/;
109
+
110
+ const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => (
111
+ Boolean(valueArg)
112
+ && typeof valueArg === 'object'
113
+ && !Array.isArray(valueArg)
114
+ && (Object.getPrototypeOf(valueArg) === Object.prototype
115
+ || Object.getPrototypeOf(valueArg) === null)
116
+ );
117
+
118
+ const hasExactKeys = (
119
+ valueArg: Record<string, unknown>,
120
+ keysArg: readonly string[],
121
+ ): boolean => {
122
+ const actual = Object.keys(valueArg).sort();
123
+ const expected = [...keysArg].sort();
124
+ return actual.length === expected.length
125
+ && actual.every((keyArg, indexArg) => keyArg === expected[indexArg]);
126
+ };
127
+
128
+ const hasKeys = (
129
+ valueArg: Record<string, unknown>,
130
+ requiredArg: readonly string[],
131
+ optionalArg: readonly string[],
132
+ ): boolean => requiredArg.every((keyArg) => Object.hasOwn(valueArg, keyArg))
133
+ && Object.keys(valueArg).every((keyArg) => requiredArg.includes(keyArg)
134
+ || optionalArg.includes(keyArg));
135
+
136
+ const isIdentifier = (valueArg: unknown): valueArg is string => (
137
+ typeof valueArg === 'string' && identifierPattern.test(valueArg)
138
+ );
139
+
140
+ const isPositiveSafeInteger = (valueArg: unknown): valueArg is number => (
141
+ Number.isSafeInteger(valueArg) && (valueArg as number) > 0
142
+ );
143
+
144
+ const isNonNegativeSafeInteger = (valueArg: unknown): valueArg is number => (
145
+ Number.isSafeInteger(valueArg) && (valueArg as number) >= 0 && !Object.is(valueArg, -0)
146
+ );
147
+
148
+ const isJwtIdentity = (valueArg: unknown): valueArg is IIdentityCredential => (
149
+ isRecord(valueArg)
150
+ && hasExactKeys(valueArg, ['jwt'])
151
+ && typeof valueArg.jwt === 'string'
152
+ && valueArg.jwt.length > 0
153
+ );
154
+
155
+ const utf8ByteLength = (valueArg: string): number => new TextEncoder().encode(valueArg).byteLength;
156
+
157
+ const isCorestoreControlToken = (valueArg: unknown): valueArg is string => {
158
+ if (typeof valueArg !== 'string' || valueArg.length === 0 || /\s/u.test(valueArg)) return false;
159
+ const encoded = new TextEncoder().encode(valueArg);
160
+ let roundTrip: string;
161
+ try {
162
+ roundTrip = new TextDecoder('utf-8', { fatal: true }).decode(encoded);
163
+ } catch {
164
+ return false;
165
+ }
166
+ return roundTrip === valueArg
167
+ && encoded.byteLength >= corestoreCredentialRuntimeLimits.minimumControlTokenBytes
168
+ && encoded.byteLength <= corestoreCredentialRuntimeLimits.maximumControlTokenBytes;
169
+ };
170
+
171
+ export const validateGetCorestoreControlCredentialMaterialRequest = (
172
+ requestArg: unknown,
173
+ activeRecipientArg: IActiveSecretRecipientMetadata,
174
+ ): string[] => {
175
+ if (!isRecord(requestArg)
176
+ || !hasExactKeys(requestArg, [
177
+ 'identity',
178
+ 'requestId',
179
+ 'capability',
180
+ 'expectedRecipientKeyId',
181
+ 'expectedRecipientGeneration',
182
+ ])) {
183
+ return ['corestore control credential request must use its exact schema'];
184
+ }
185
+ const errors: string[] = [];
186
+ if (!isJwtIdentity(requestArg.identity)) {
187
+ errors.push('corestore control credential request requires only a JWT identity');
188
+ }
189
+ if (!isIdentifier(requestArg.requestId)
190
+ || !isIdentifier(requestArg.expectedRecipientKeyId)) {
191
+ errors.push('corestore control credential request identifiers must be canonical');
192
+ }
193
+ if (requestArg.capability !== 'database' && requestArg.capability !== 'objectstorage') {
194
+ errors.push('corestore control credential capability must be database or objectstorage');
195
+ }
196
+ if (!isPositiveSafeInteger(requestArg.expectedRecipientGeneration)) {
197
+ errors.push('corestore control credential recipient generation must be positive');
198
+ }
199
+ if (validateSecretRecipientMetadata(activeRecipientArg).length > 0
200
+ || activeRecipientArg.lifecycleState !== 'active'
201
+ || requestArg.expectedRecipientKeyId !== activeRecipientArg.recipientKeyId
202
+ || requestArg.expectedRecipientGeneration !== activeRecipientArg.generation) {
203
+ errors.push('corestore control credential request must target the active recipient');
204
+ }
205
+ return errors;
206
+ };
207
+
208
+ export const createCorestoreControlCredentialMaterialEnvelopeContext = (
209
+ materialArg: ICorestoreControlCredentialMaterialContextInputV1,
210
+ ): Uint8Array => new TextEncoder().encode(JSON.stringify({
211
+ schemaVersion: 1,
212
+ purpose: 'serve.zone/corestore-control-credential-material',
213
+ requestId: materialArg.requestId,
214
+ clusterId: materialArg.clusterId,
215
+ capability: materialArg.capability,
216
+ providerConfigId: getCorestoreProviderConfigId(materialArg.capability),
217
+ controlUrl: corestoreControlUrl,
218
+ managementScope: corestoreCredentialManagementScope,
219
+ key: corestoreControlCredentialKey,
220
+ environment: corestoreCredentialEnvironment,
221
+ recipientKeyId: materialArg.recipientKeyId,
222
+ recipientGeneration: materialArg.recipientGeneration,
223
+ }));
224
+
225
+ export const validateCorestoreControlCredentialPlaintext = (
226
+ plaintextArg: unknown,
227
+ ): string[] => {
228
+ if (!isRecord(plaintextArg)
229
+ || !hasExactKeys(plaintextArg, ['schemaVersion', 'key', 'environment', 'value'])) {
230
+ return ['corestore control credential plaintext must use its exact schema'];
231
+ }
232
+ const errors: string[] = [];
233
+ if (plaintextArg.schemaVersion !== 1
234
+ || plaintextArg.key !== corestoreControlCredentialKey
235
+ || plaintextArg.environment !== corestoreCredentialEnvironment) {
236
+ errors.push('corestore control credential plaintext constants must be canonical');
237
+ }
238
+ if (!isCorestoreControlToken(plaintextArg.value)) {
239
+ errors.push('corestore control credential value must be 32-4096 UTF-8 bytes without whitespace');
240
+ }
241
+ return errors;
242
+ };
243
+
244
+ export const validateCorestoreControlCredentialMaterial = async (
245
+ materialArg: unknown,
246
+ expectedArg: ICorestoreControlCredentialMaterialExpectationV1,
247
+ ): Promise<string[]> => {
248
+ if (!isRecord(materialArg)
249
+ || !hasExactKeys(materialArg, [
250
+ 'schemaVersion',
251
+ 'requestId',
252
+ 'clusterId',
253
+ 'capability',
254
+ 'providerConfigId',
255
+ 'controlUrl',
256
+ 'managementScope',
257
+ 'key',
258
+ 'environment',
259
+ 'recipientKeyId',
260
+ 'recipientGeneration',
261
+ 'envelope',
262
+ ])) {
263
+ return ['corestore control credential material must use its exact schema'];
264
+ }
265
+ const errors: string[] = [];
266
+ if (materialArg.schemaVersion !== 1
267
+ || !isIdentifier(materialArg.requestId)
268
+ || !isIdentifier(materialArg.clusterId)
269
+ || !isIdentifier(materialArg.recipientKeyId)
270
+ || !isPositiveSafeInteger(materialArg.recipientGeneration)) {
271
+ errors.push('corestore control credential material metadata must be canonical');
272
+ }
273
+ if (materialArg.capability !== 'database' && materialArg.capability !== 'objectstorage') {
274
+ errors.push('corestore control credential material capability must be canonical');
275
+ } else if (materialArg.providerConfigId !== getCorestoreProviderConfigId(materialArg.capability)) {
276
+ errors.push('corestore control credential provider must be derived from capability');
277
+ }
278
+ if (materialArg.controlUrl !== corestoreControlUrl
279
+ || materialArg.managementScope !== corestoreCredentialManagementScope
280
+ || materialArg.key !== corestoreControlCredentialKey
281
+ || materialArg.environment !== corestoreCredentialEnvironment) {
282
+ errors.push('corestore control credential material constants must be canonical');
283
+ }
284
+ if (materialArg.requestId !== expectedArg.requestId
285
+ || materialArg.clusterId !== expectedArg.clusterId
286
+ || materialArg.capability !== expectedArg.capability
287
+ || materialArg.recipientKeyId !== expectedArg.expectedRecipientKeyId
288
+ || materialArg.recipientGeneration !== expectedArg.expectedRecipientGeneration) {
289
+ errors.push('corestore control credential material does not match its request authority');
290
+ }
291
+ let envelope: plugins.smartcrypto.IX25519EnvelopeV1 | undefined;
292
+ try {
293
+ envelope = plugins.smartcrypto.parseX25519Envelope(materialArg.envelope);
294
+ if (envelope.ciphertext.length
295
+ > Math.ceil(corestoreCredentialRuntimeLimits.maximumSealedControlCredentialBytes * 4 / 3)) {
296
+ errors.push('corestore control credential envelope exceeds its byte limit');
297
+ }
298
+ } catch {
299
+ errors.push('corestore control credential envelope must be strict');
300
+ }
301
+ if (envelope) {
302
+ if (envelope.recipientKeyId !== materialArg.recipientKeyId) {
303
+ errors.push('corestore control credential envelope recipient must match');
304
+ } else if (errors.length === 0 && !await verifySecretEnvelopeContext(
305
+ envelope,
306
+ createCorestoreControlCredentialMaterialEnvelopeContext(
307
+ materialArg as unknown as ICorestoreControlCredentialMaterialContextInputV1,
308
+ ),
309
+ )) {
310
+ errors.push('corestore control credential envelope context must match');
311
+ }
312
+ }
313
+ return errors;
314
+ };
315
+
316
+ export interface ICorestoreCredentialPublicationGrantV1 {
317
+ schemaVersion: 1;
318
+ operationId: string;
319
+ serviceId: string;
320
+ bindingId: string;
321
+ capability: TCorestoreCredentialCapability;
322
+ reconciliationGeneration: number;
323
+ expectedTargetSecretsRevision: number;
324
+ bindingRequestDigest: string;
325
+ issuedAt: number;
326
+ expiresAt: number;
327
+ }
328
+
329
+ export const validateCorestoreCredentialPublicationGrant = (
330
+ grantArg: unknown,
331
+ trustedNowArg?: number,
332
+ ): string[] => {
333
+ if (!isRecord(grantArg)
334
+ || !hasExactKeys(grantArg, [
335
+ 'schemaVersion',
336
+ 'operationId',
337
+ 'serviceId',
338
+ 'bindingId',
339
+ 'capability',
340
+ 'reconciliationGeneration',
341
+ 'expectedTargetSecretsRevision',
342
+ 'bindingRequestDigest',
343
+ 'issuedAt',
344
+ 'expiresAt',
345
+ ])) {
346
+ return ['corestore credential publication grant must use its exact schema'];
347
+ }
348
+ const errors: string[] = [];
349
+ if (grantArg.schemaVersion !== 1) {
350
+ errors.push('corestore credential publication grant schemaVersion must be 1');
351
+ }
352
+ if (!isIdentifier(grantArg.operationId)
353
+ || !isIdentifier(grantArg.serviceId)
354
+ || !isIdentifier(grantArg.bindingId)) {
355
+ errors.push('corestore credential publication grant identifiers must be canonical');
356
+ }
357
+ if (grantArg.capability !== 'database' && grantArg.capability !== 'objectstorage') {
358
+ errors.push('corestore credential publication grant capability must be canonical');
359
+ }
360
+ if (!isPositiveSafeInteger(grantArg.reconciliationGeneration)
361
+ || !isNonNegativeSafeInteger(grantArg.expectedTargetSecretsRevision)) {
362
+ errors.push('corestore credential publication grant revisions must be canonical');
363
+ }
364
+ if (typeof grantArg.bindingRequestDigest !== 'string'
365
+ || !bareSha256Pattern.test(grantArg.bindingRequestDigest)) {
366
+ errors.push('corestore credential publication grant digest must be bare lowercase 64-hex');
367
+ }
368
+ if (!isPositiveSafeInteger(grantArg.issuedAt)
369
+ || !isPositiveSafeInteger(grantArg.expiresAt)
370
+ || (grantArg.expiresAt as number) <= (grantArg.issuedAt as number)
371
+ || (grantArg.expiresAt as number) - (grantArg.issuedAt as number)
372
+ > corestoreCredentialRuntimeLimits.maximumGrantLifetimeMs) {
373
+ errors.push('corestore credential publication grant lifetime must be positive and bounded');
374
+ }
375
+ if (trustedNowArg !== undefined
376
+ && (!isPositiveSafeInteger(trustedNowArg)
377
+ || trustedNowArg < (grantArg.issuedAt as number)
378
+ || trustedNowArg >= (grantArg.expiresAt as number))) {
379
+ errors.push('corestore credential publication grant must be currently valid');
380
+ }
381
+ return errors;
382
+ };
383
+
384
+ export const corestoreCredentialPublicationGrantsEqual = (
385
+ leftArg: unknown,
386
+ rightArg: unknown,
387
+ ): boolean => validateCorestoreCredentialPublicationGrant(leftArg).length === 0
388
+ && validateCorestoreCredentialPublicationGrant(rightArg).length === 0
389
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).schemaVersion
390
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).schemaVersion
391
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).operationId
392
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).operationId
393
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).serviceId
394
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).serviceId
395
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).bindingId
396
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).bindingId
397
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).capability
398
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).capability
399
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).reconciliationGeneration
400
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).reconciliationGeneration
401
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).expectedTargetSecretsRevision
402
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).expectedTargetSecretsRevision
403
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).bindingRequestDigest
404
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).bindingRequestDigest
405
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).issuedAt
406
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).issuedAt
407
+ && (leftArg as ICorestoreCredentialPublicationGrantV1).expiresAt
408
+ === (rightArg as ICorestoreCredentialPublicationGrantV1).expiresAt;
409
+
410
+ const comparePublicationGrants = (
411
+ leftArg: ICorestoreCredentialPublicationGrantV1,
412
+ rightArg: ICorestoreCredentialPublicationGrantV1,
413
+ ): number => leftArg.bindingId < rightArg.bindingId
414
+ ? -1
415
+ : leftArg.bindingId > rightArg.bindingId
416
+ ? 1
417
+ : leftArg.operationId < rightArg.operationId
418
+ ? -1
419
+ : leftArg.operationId > rightArg.operationId
420
+ ? 1
421
+ : 0;
422
+
423
+ export const validateCorestoreCredentialPublicationGrants = (
424
+ grantsArg: unknown,
425
+ trustedNowArg?: number,
426
+ ): string[] => {
427
+ if (!Array.isArray(grantsArg)
428
+ || grantsArg.length > corestoreCredentialRuntimeLimits.maximumPublicationGrants) {
429
+ return ['corestore credential publication grants must be a bounded array'];
430
+ }
431
+ const errors: string[] = [];
432
+ let previous: ICorestoreCredentialPublicationGrantV1 | undefined;
433
+ for (const [index, grantArg] of grantsArg.entries()) {
434
+ const grantErrors = validateCorestoreCredentialPublicationGrant(grantArg, trustedNowArg);
435
+ errors.push(...grantErrors.map((errorArg) => `publication grants[${index}] ${errorArg}`));
436
+ if (grantErrors.length === 0) {
437
+ const grant = grantArg as ICorestoreCredentialPublicationGrantV1;
438
+ if (previous && comparePublicationGrants(previous, grant) >= 0) {
439
+ errors.push('corestore credential publication grants must be unique and sorted by bindingId then operationId');
440
+ }
441
+ previous = grant;
442
+ }
443
+ }
444
+ return errors;
445
+ };
446
+
447
+ export const corestoreDatabaseCredentialKeys = Object.freeze([
448
+ 'MONGODB_URI',
449
+ 'MONGODB_URL',
450
+ 'MONGO_URL',
451
+ 'MONGODB_HOST',
452
+ 'MONGODB_PORT',
453
+ 'MONGODB_DATABASE',
454
+ 'MONGO_DBNAME',
455
+ 'MONGODB_USERNAME',
456
+ 'MONGO_DBUSER',
457
+ 'MONGODB_PASSWORD',
458
+ 'MONGO_DBPASS',
459
+ ] as const);
460
+ export const corestoreObjectStorageCredentialKeys = Object.freeze([
461
+ 'accessKeyId',
462
+ 'secretAccessKey',
463
+ ] as const);
464
+
465
+ export type TCorestoreDatabaseCredentialKey = typeof corestoreDatabaseCredentialKeys[number];
466
+ export type TCorestoreObjectStorageCredentialKey =
467
+ typeof corestoreObjectStorageCredentialKeys[number];
468
+ export type TCorestoreCredentialKey =
469
+ | TCorestoreDatabaseCredentialKey
470
+ | TCorestoreObjectStorageCredentialKey;
471
+
472
+ export type TCorestoreDatabaseCredentialValuesV1 = {
473
+ [TKey in TCorestoreDatabaseCredentialKey]: string;
474
+ };
475
+
476
+ export interface ICorestoreDatabaseCredentialPlaintextMaterialV1 {
477
+ schemaVersion: 1;
478
+ capability: 'database';
479
+ values: TCorestoreDatabaseCredentialValuesV1;
480
+ }
481
+
482
+ export interface ICorestoreObjectStorageCredentialPlaintextMaterialV1 {
483
+ schemaVersion: 1;
484
+ capability: 'objectstorage';
485
+ values: {
486
+ accessKeyId: string;
487
+ secretAccessKey: string;
488
+ };
489
+ retention?: IObjectStorageRetentionEvidenceV1;
490
+ }
491
+
492
+ export type TCorestoreCredentialPlaintextMaterialV1 =
493
+ | ICorestoreDatabaseCredentialPlaintextMaterialV1
494
+ | ICorestoreObjectStorageCredentialPlaintextMaterialV1;
495
+
496
+ const readCredentialString = (valueArg: unknown): boolean => typeof valueArg === 'string'
497
+ && valueArg.length > 0
498
+ && utf8ByteLength(valueArg) <= corestoreCredentialRuntimeLimits.maximumCredentialStringBytes
499
+ && !valueArg.includes('\0');
500
+
501
+ export const validateCorestoreDatabaseCredentialMaterial = (
502
+ materialArg: unknown,
503
+ ): string[] => {
504
+ if (!isRecord(materialArg)
505
+ || !hasExactKeys(materialArg, ['schemaVersion', 'capability', 'values'])
506
+ || materialArg.schemaVersion !== 1
507
+ || materialArg.capability !== 'database'
508
+ || !isRecord(materialArg.values)
509
+ || !hasExactKeys(materialArg.values, corestoreDatabaseCredentialKeys)) {
510
+ return ['corestore database credential material must use its exact schema'];
511
+ }
512
+ const values = materialArg.values;
513
+ const errors: string[] = [];
514
+ if (corestoreDatabaseCredentialKeys.some((keyArg) => !readCredentialString(values[keyArg]))) {
515
+ errors.push('corestore database credential values must be bounded non-empty strings');
516
+ }
517
+ if (values.MONGODB_URI !== values.MONGODB_URL
518
+ || values.MONGODB_URI !== values.MONGO_URL) {
519
+ errors.push('corestore database URI aliases must be equal');
520
+ }
521
+ if (values.MONGODB_DATABASE !== values.MONGO_DBNAME) {
522
+ errors.push('corestore database name aliases must be equal');
523
+ }
524
+ if (values.MONGODB_USERNAME !== values.MONGO_DBUSER) {
525
+ errors.push('corestore database username aliases must be equal');
526
+ }
527
+ if (values.MONGODB_PASSWORD !== values.MONGO_DBPASS) {
528
+ errors.push('corestore database password aliases must be equal');
529
+ }
530
+ if (typeof values.MONGODB_PORT !== 'string'
531
+ || !/^[1-9][0-9]{0,4}$/.test(values.MONGODB_PORT)
532
+ || Number(values.MONGODB_PORT) > 65535) {
533
+ errors.push('corestore database port must be canonical');
534
+ }
535
+ return errors;
536
+ };
537
+
538
+ export const validateCorestoreObjectStorageCredentialMaterial = async (
539
+ materialArg: unknown,
540
+ retentionExpectationArg?: IObjectStorageRetentionEvidenceExpectationV1,
541
+ ): Promise<string[]> => {
542
+ if (!isRecord(materialArg)
543
+ || !hasKeys(materialArg, ['schemaVersion', 'capability', 'values'], ['retention'])
544
+ || materialArg.schemaVersion !== 1
545
+ || materialArg.capability !== 'objectstorage'
546
+ || !isRecord(materialArg.values)
547
+ || !hasExactKeys(materialArg.values, corestoreObjectStorageCredentialKeys)) {
548
+ return ['corestore objectstorage credential material must use its exact schema'];
549
+ }
550
+ const errors: string[] = [];
551
+ if (!readCredentialString(materialArg.values.accessKeyId)
552
+ || !readCredentialString(materialArg.values.secretAccessKey)) {
553
+ errors.push('corestore objectstorage credential values must be bounded non-empty strings');
554
+ }
555
+ if (materialArg.retention !== undefined) {
556
+ if (!retentionExpectationArg) {
557
+ errors.push('corestore objectstorage retention evidence requires trusted binding authority');
558
+ } else {
559
+ errors.push(...await validateObjectStorageRetentionEvidence(
560
+ materialArg.retention,
561
+ retentionExpectationArg,
562
+ ));
563
+ }
564
+ }
565
+ return errors;
566
+ };
567
+
568
+ export const validateCorestoreCredentialPlaintextMaterial = async (
569
+ materialArg: unknown,
570
+ retentionExpectationArg?: IObjectStorageRetentionEvidenceExpectationV1,
571
+ ): Promise<string[]> => isRecord(materialArg) && materialArg.capability === 'database'
572
+ ? validateCorestoreDatabaseCredentialMaterial(materialArg)
573
+ : validateCorestoreObjectStorageCredentialMaterial(materialArg, retentionExpectationArg);
574
+
575
+ export interface IPublishCorestoreCredentialMaterialRequestV1 {
576
+ identity: IIdentityCredential;
577
+ mutationId: string;
578
+ grant: ICorestoreCredentialPublicationGrantV1;
579
+ expectedIngressRecipientKeyId: string;
580
+ expectedIngressRecipientGeneration: number;
581
+ envelope: plugins.smartcrypto.IX25519EnvelopeV1;
582
+ }
583
+
584
+ export interface ICorestoreCredentialSecretVersionReferenceV1
585
+ extends ISecretVersionReference {
586
+ key: TCorestoreCredentialKey;
587
+ }
588
+
589
+ export interface ICorestoreCredentialPublicationCoverageV1 {
590
+ keys: TCorestoreCredentialKey[];
591
+ versionReferences: ICorestoreCredentialSecretVersionReferenceV1[];
592
+ }
593
+
594
+ export interface ICorestoreCredentialPublicationReceiptV1 {
595
+ schemaVersion: 1;
596
+ mutationId: string;
597
+ organizationId: string;
598
+ clusterId: string;
599
+ serviceId: string;
600
+ bindingId: string;
601
+ capability: TCorestoreCredentialCapability;
602
+ grant: ICorestoreCredentialPublicationGrantV1;
603
+ coverage: ICorestoreCredentialPublicationCoverageV1;
604
+ expectedTargetSecretsRevision: number;
605
+ targetSecretsRevision: number;
606
+ ingressAdmissionBinding: ISecretEnvelopeAdmissionBindingV1;
607
+ acceptedAt: number;
608
+ replayed: boolean;
609
+ }
610
+
611
+ export interface IReq_PublishCorestoreCredentialMaterial
612
+ extends plugins.typedrequestInterfaces.implementsTR<
613
+ plugins.typedrequestInterfaces.ITypedRequest,
614
+ IReq_PublishCorestoreCredentialMaterial
615
+ > {
616
+ method: 'publishCorestoreCredentialMaterial';
617
+ request: IPublishCorestoreCredentialMaterialRequestV1;
618
+ response: {
619
+ receipt: ICorestoreCredentialPublicationReceiptV1;
620
+ };
621
+ }
622
+
623
+ export const createPublishCorestoreCredentialMaterialEnvelopeContext = (
624
+ requestArg: Omit<IPublishCorestoreCredentialMaterialRequestV1, 'identity' | 'envelope'>,
625
+ ): Uint8Array => new TextEncoder().encode(JSON.stringify({
626
+ schemaVersion: 1,
627
+ purpose: 'serve.zone/publish-corestore-credential-material',
628
+ mutationId: requestArg.mutationId,
629
+ grant: {
630
+ schemaVersion: requestArg.grant.schemaVersion,
631
+ operationId: requestArg.grant.operationId,
632
+ serviceId: requestArg.grant.serviceId,
633
+ bindingId: requestArg.grant.bindingId,
634
+ capability: requestArg.grant.capability,
635
+ reconciliationGeneration: requestArg.grant.reconciliationGeneration,
636
+ expectedTargetSecretsRevision: requestArg.grant.expectedTargetSecretsRevision,
637
+ bindingRequestDigest: requestArg.grant.bindingRequestDigest,
638
+ issuedAt: requestArg.grant.issuedAt,
639
+ expiresAt: requestArg.grant.expiresAt,
640
+ },
641
+ expectedIngressRecipientKeyId: requestArg.expectedIngressRecipientKeyId,
642
+ expectedIngressRecipientGeneration: requestArg.expectedIngressRecipientGeneration,
643
+ }));
644
+
645
+ export const validatePublishCorestoreCredentialMaterialRequest = async (
646
+ requestArg: unknown,
647
+ expectedGrantArg: ICorestoreCredentialPublicationGrantV1,
648
+ activeRecipientArg: IActiveSecretRecipientMetadata,
649
+ trustedNowArg: number,
650
+ ): Promise<string[]> => {
651
+ if (!isRecord(requestArg)
652
+ || !hasExactKeys(requestArg, [
653
+ 'identity',
654
+ 'mutationId',
655
+ 'grant',
656
+ 'expectedIngressRecipientKeyId',
657
+ 'expectedIngressRecipientGeneration',
658
+ 'envelope',
659
+ ])) {
660
+ return ['publish corestore credential request must use its exact schema'];
661
+ }
662
+ const errors = validateCorestoreCredentialPublicationGrant(requestArg.grant, trustedNowArg);
663
+ if (!corestoreCredentialPublicationGrantsEqual(requestArg.grant, expectedGrantArg)) {
664
+ errors.push('publish corestore credential request grant must match current server authority');
665
+ }
666
+ if (!isJwtIdentity(requestArg.identity)) {
667
+ errors.push('publish corestore credential request requires only a JWT identity');
668
+ }
669
+ if (!isIdentifier(requestArg.mutationId)
670
+ || !isIdentifier(requestArg.expectedIngressRecipientKeyId)
671
+ || !isPositiveSafeInteger(requestArg.expectedIngressRecipientGeneration)) {
672
+ errors.push('publish corestore credential request recipient and mutation must be canonical');
673
+ }
674
+ let envelope: plugins.smartcrypto.IX25519EnvelopeV1 | undefined;
675
+ try {
676
+ envelope = plugins.smartcrypto.parseX25519Envelope(requestArg.envelope);
677
+ if (envelope.ciphertext.length
678
+ > Math.ceil(corestoreCredentialRuntimeLimits.maximumSealedMaterialBytes * 4 / 3)) {
679
+ errors.push('publish corestore credential envelope exceeds its byte limit');
680
+ }
681
+ } catch {
682
+ errors.push('publish corestore credential envelope must be strict');
683
+ }
684
+ if (validateSecretRecipientMetadata(activeRecipientArg).length > 0
685
+ || activeRecipientArg.lifecycleState !== 'active'
686
+ || activeRecipientArg.recipientKeyId !== requestArg.expectedIngressRecipientKeyId
687
+ || activeRecipientArg.generation !== requestArg.expectedIngressRecipientGeneration) {
688
+ errors.push('publish corestore credential request must target the active ingress recipient');
689
+ }
690
+ if (envelope) {
691
+ if (envelope.recipientKeyId !== requestArg.expectedIngressRecipientKeyId) {
692
+ errors.push('publish corestore credential envelope recipient must match');
693
+ } else if (errors.length === 0 && !await verifySecretEnvelopeContext(
694
+ envelope,
695
+ createPublishCorestoreCredentialMaterialEnvelopeContext(
696
+ requestArg as unknown as Omit<
697
+ IPublishCorestoreCredentialMaterialRequestV1,
698
+ 'identity' | 'envelope'
699
+ >,
700
+ ),
701
+ )) {
702
+ errors.push('publish corestore credential envelope context must match');
703
+ }
704
+ }
705
+ return errors;
706
+ };
707
+
708
+ const expectedCoverageKeys = (
709
+ capabilityArg: TCorestoreCredentialCapability,
710
+ ): readonly TCorestoreCredentialKey[] => capabilityArg === 'database'
711
+ ? corestoreDatabaseCredentialKeys
712
+ : corestoreObjectStorageCredentialKeys;
713
+
714
+ export interface ICorestoreCredentialPublicationReceiptExpectationV1 {
715
+ request: IPublishCorestoreCredentialMaterialRequestV1;
716
+ organizationId: string;
717
+ clusterId: string;
718
+ }
719
+
720
+ const computeRuntimeSha256 = async (valueArg: Uint8Array): Promise<string> => {
721
+ const digest = new Uint8Array(await globalThis.crypto.subtle.digest(
722
+ 'SHA-256',
723
+ new Uint8Array(valueArg),
724
+ ));
725
+ return [...digest].map((byteArg) => byteArg.toString(16).padStart(2, '0')).join('');
726
+ };
727
+
728
+ const verifyPublicationAdmissionBinding = async (
729
+ bindingArg: ISecretEnvelopeAdmissionBindingV1,
730
+ envelopeArg: unknown,
731
+ contextArg: Uint8Array,
732
+ ): Promise<boolean> => {
733
+ try {
734
+ const envelope = plugins.smartcrypto.parseX25519Envelope(envelopeArg);
735
+ if (envelope.recipientKeyId !== bindingArg.recipientKeyId
736
+ || !await verifySecretEnvelopeContext(envelope, contextArg)) {
737
+ return false;
738
+ }
739
+ const envelopeDigestInput = JSON.stringify({
740
+ schemaVersion: envelope.schemaVersion,
741
+ profile: envelope.profile,
742
+ recipientKeyId: envelope.recipientKeyId,
743
+ ephemeralPublicKey: envelope.ephemeralPublicKey,
744
+ nonce: envelope.nonce,
745
+ ciphertext: envelope.ciphertext,
746
+ tag: envelope.tag,
747
+ contextDigest: envelope.contextDigest,
748
+ });
749
+ return bindingArg.envelopeDigest
750
+ === `sha256:${await computeRuntimeSha256(new TextEncoder().encode(envelopeDigestInput))}`
751
+ && bindingArg.requestContextDigest
752
+ === `sha256:${await computeRuntimeSha256(contextArg)}`;
753
+ } catch {
754
+ return false;
755
+ }
756
+ };
757
+
758
+ export const validateCorestoreCredentialPublicationReceipt = async (
759
+ receiptArg: unknown,
760
+ expectedArg: ICorestoreCredentialPublicationReceiptExpectationV1,
761
+ ): Promise<string[]> => {
762
+ if (!isRecord(receiptArg)
763
+ || !hasExactKeys(receiptArg, [
764
+ 'schemaVersion',
765
+ 'mutationId',
766
+ 'organizationId',
767
+ 'clusterId',
768
+ 'serviceId',
769
+ 'bindingId',
770
+ 'capability',
771
+ 'grant',
772
+ 'coverage',
773
+ 'expectedTargetSecretsRevision',
774
+ 'targetSecretsRevision',
775
+ 'ingressAdmissionBinding',
776
+ 'acceptedAt',
777
+ 'replayed',
778
+ ])) {
779
+ return ['corestore credential publication receipt must use its exact schema'];
780
+ }
781
+ const errors = validateCorestoreCredentialPublicationGrant(receiptArg.grant);
782
+ if (receiptArg.schemaVersion !== 1
783
+ || !isIdentifier(receiptArg.mutationId)
784
+ || !isIdentifier(receiptArg.organizationId)
785
+ || !isIdentifier(receiptArg.clusterId)
786
+ || !isIdentifier(receiptArg.serviceId)
787
+ || !isIdentifier(receiptArg.bindingId)) {
788
+ errors.push('corestore credential publication receipt identifiers must be canonical');
789
+ }
790
+ const grant = receiptArg.grant as ICorestoreCredentialPublicationGrantV1;
791
+ if ((receiptArg.capability !== 'database' && receiptArg.capability !== 'objectstorage')
792
+ || receiptArg.serviceId !== grant.serviceId
793
+ || receiptArg.bindingId !== grant.bindingId
794
+ || receiptArg.capability !== grant.capability
795
+ || receiptArg.expectedTargetSecretsRevision !== grant.expectedTargetSecretsRevision) {
796
+ errors.push('corestore credential publication receipt must exactly bind its grant');
797
+ }
798
+ if (receiptArg.mutationId !== expectedArg.request.mutationId
799
+ || receiptArg.organizationId !== expectedArg.organizationId
800
+ || receiptArg.clusterId !== expectedArg.clusterId
801
+ || !corestoreCredentialPublicationGrantsEqual(receiptArg.grant, expectedArg.request.grant)) {
802
+ errors.push('corestore credential publication receipt must match its trusted request scope');
803
+ }
804
+ if (!isRecord(receiptArg.coverage)
805
+ || !hasExactKeys(receiptArg.coverage, ['keys', 'versionReferences'])
806
+ || !Array.isArray(receiptArg.coverage.keys)
807
+ || !Array.isArray(receiptArg.coverage.versionReferences)) {
808
+ errors.push('corestore credential publication coverage must use its exact schema');
809
+ } else if (receiptArg.capability === 'database' || receiptArg.capability === 'objectstorage') {
810
+ const requiredKeys = expectedCoverageKeys(receiptArg.capability);
811
+ if (JSON.stringify(receiptArg.coverage.keys) !== JSON.stringify(requiredKeys)
812
+ || receiptArg.coverage.versionReferences.length !== requiredKeys.length) {
813
+ errors.push('corestore credential publication coverage must be exact');
814
+ } else {
815
+ const secretIds = new Set<string>();
816
+ const secretVersionIds = new Set<string>();
817
+ for (const [index, referenceArg] of receiptArg.coverage.versionReferences.entries()) {
818
+ if (!isRecord(referenceArg)
819
+ || !hasExactKeys(referenceArg, ['key', 'secretId', 'secretVersionId'])
820
+ || referenceArg.key !== requiredKeys[index]
821
+ || !isIdentifier(referenceArg.secretId)
822
+ || !isIdentifier(referenceArg.secretVersionId)) {
823
+ errors.push('corestore credential publication version references must exactly cover keys');
824
+ break;
825
+ }
826
+ if (secretIds.has(referenceArg.secretId as string)
827
+ || secretVersionIds.has(referenceArg.secretVersionId as string)) {
828
+ errors.push('corestore credential publication version references must be unique');
829
+ break;
830
+ }
831
+ secretIds.add(referenceArg.secretId as string);
832
+ secretVersionIds.add(referenceArg.secretVersionId as string);
833
+ }
834
+ }
835
+ }
836
+ const referenceCount = isRecord(receiptArg.coverage)
837
+ && Array.isArray(receiptArg.coverage.versionReferences)
838
+ ? receiptArg.coverage.versionReferences.length
839
+ : 0;
840
+ if (!isNonNegativeSafeInteger(receiptArg.expectedTargetSecretsRevision)
841
+ || !isNonNegativeSafeInteger(receiptArg.targetSecretsRevision)
842
+ || receiptArg.targetSecretsRevision !== receiptArg.expectedTargetSecretsRevision + referenceCount) {
843
+ errors.push('corestore credential publication target Secrets revision must exactly cover created versions');
844
+ }
845
+ const admission = receiptArg.ingressAdmissionBinding;
846
+ if (!isRecord(admission)
847
+ || !hasExactKeys(admission, [
848
+ 'schemaVersion',
849
+ 'recipientKeyId',
850
+ 'recipientGeneration',
851
+ 'envelopeDigest',
852
+ 'requestContextDigest',
853
+ ])
854
+ || admission.schemaVersion !== 1
855
+ || !isIdentifier(admission.recipientKeyId)
856
+ || !isPositiveSafeInteger(admission.recipientGeneration)
857
+ || typeof admission.envelopeDigest !== 'string'
858
+ || !isSha256Digest(admission.envelopeDigest)
859
+ || typeof admission.requestContextDigest !== 'string'
860
+ || !isSha256Digest(admission.requestContextDigest)) {
861
+ errors.push('corestore credential publication ingress admission binding must be canonical');
862
+ } else if (admission.recipientKeyId !== expectedArg.request.expectedIngressRecipientKeyId
863
+ || admission.recipientGeneration !== expectedArg.request.expectedIngressRecipientGeneration
864
+ || !await verifyPublicationAdmissionBinding(
865
+ admission as unknown as ISecretEnvelopeAdmissionBindingV1,
866
+ expectedArg.request.envelope,
867
+ createPublishCorestoreCredentialMaterialEnvelopeContext(expectedArg.request),
868
+ )) {
869
+ errors.push('corestore credential publication ingress admission binding must match the request');
870
+ }
871
+ if (!isPositiveSafeInteger(receiptArg.acceptedAt)
872
+ || receiptArg.acceptedAt < grant.issuedAt
873
+ || receiptArg.acceptedAt >= grant.expiresAt
874
+ || typeof receiptArg.replayed !== 'boolean') {
875
+ errors.push('corestore credential publication receipt acceptance must be canonical');
876
+ }
877
+ return errors;
878
+ };
879
+
880
+ export const corestoreCredentialPublicationErrorMetadata = Object.freeze({
881
+ INVALID_IDENTITY: Object.freeze({ retryable: false }),
882
+ INVALID_REQUEST: Object.freeze({ retryable: false }),
883
+ GRANT_EXPIRED: Object.freeze({ retryable: false }),
884
+ GRANT_SCOPE_MISMATCH: Object.freeze({ retryable: false }),
885
+ GRANT_REVISION_MISMATCH: Object.freeze({ retryable: true }),
886
+ INGRESS_RECIPIENT_MISMATCH: Object.freeze({ retryable: true }),
887
+ ENVELOPE_INVALID: Object.freeze({ retryable: false }),
888
+ MATERIAL_INVALID: Object.freeze({ retryable: false }),
889
+ SECRETS_REVISION_CONFLICT: Object.freeze({ retryable: true }),
890
+ REPLAY_CONFLICT: Object.freeze({ retryable: false }),
891
+ INTERNAL_ERROR: Object.freeze({ retryable: true }),
892
+ } as const);
893
+
894
+ export type TCorestoreCredentialPublicationErrorCode =
895
+ keyof typeof corestoreCredentialPublicationErrorMetadata;
896
+
897
+ export interface ICorestoreCredentialPublicationErrorMetadataV1 {
898
+ schemaVersion: 1;
899
+ code: TCorestoreCredentialPublicationErrorCode;
900
+ retryable: boolean;
901
+ }
902
+
903
+ export const getCorestoreCredentialPublicationErrorMetadata = (
904
+ codeArg: TCorestoreCredentialPublicationErrorCode,
905
+ ): ICorestoreCredentialPublicationErrorMetadataV1 => ({
906
+ schemaVersion: 1,
907
+ code: codeArg,
908
+ retryable: corestoreCredentialPublicationErrorMetadata[codeArg].retryable,
909
+ });