import * as plugins from './plugins.js'; import type { IActiveSecretRecipientMetadata, IIdentityCredential, ISecretVersionReference, } from './data/index.js'; import { isSha256Digest } from './data/immutableimage.js'; import { validateSecretRecipientMetadata, verifySecretEnvelopeContext, } from './data/secret.js'; import type { ISecretEnvelopeAdmissionBindingV1 } from './runtime.js'; import type { IObjectStorageRetentionIntentV1, IObjectStorageRetentionEvidenceExpectationV1, IObjectStorageRetentionEvidenceV1, } from './platform/objectstorageretention.js'; import { validateObjectStorageRetentionEvidence, validateObjectStorageRetentionIntent, } from './platform/objectstorageretention.js'; import { validatePlatformObjectStorageBucketName } from './platform/objectstorage.js'; import { canonicalizeStrictJson, createCanonicalJsonSha256Hex, deepFreezeValue, } from './private/canonicaljson.js'; export type TCorestoreCredentialCapability = 'database' | 'objectstorage'; export const corestoreProviderConfigIds = Object.freeze({ database: 'cloudly-corestore-database', objectstorage: 'cloudly-corestore-objectstorage', } as const); export const corestoreControlUrl = 'http://corestore:3000' as const; export const corestoreCredentialManagementScope = 'platform:cloudly-corestore' as const; export const corestoreControlCredentialKey = 'CORESTORE_API_TOKEN' as const; export const corestoreCredentialEnvironment = 'production' as const; export const corestoreCredentialRuntimeLimits = Object.freeze({ maximumIdentifierLength: 200, maximumPublicationGrants: 256, maximumGrantLifetimeMs: 24 * 60 * 60 * 1000, maximumCredentialStringBytes: 8 * 1024, maximumSealedMaterialBytes: 64 * 1024, minimumControlTokenBytes: 32, maximumControlTokenBytes: 4096, maximumSealedControlCredentialBytes: 32 * 1024, }); export const getCorestoreProviderConfigId = ( capabilityArg: TCorestoreCredentialCapability, ): typeof corestoreProviderConfigIds[TCorestoreCredentialCapability] => { if (capabilityArg !== 'database' && capabilityArg !== 'objectstorage') { throw new Error('corestore capability must be database or objectstorage'); } return corestoreProviderConfigIds[capabilityArg]; }; export interface ICorestoreControlCredentialPlaintextV1 { schemaVersion: 1; key: typeof corestoreControlCredentialKey; environment: typeof corestoreCredentialEnvironment; value: string; } export interface ICorestoreControlCredentialMaterialV1 { schemaVersion: 1; requestId: string; clusterId: string; capability: TCorestoreCredentialCapability; providerConfigId: typeof corestoreProviderConfigIds[TCorestoreCredentialCapability]; controlUrl: typeof corestoreControlUrl; managementScope: typeof corestoreCredentialManagementScope; key: typeof corestoreControlCredentialKey; environment: typeof corestoreCredentialEnvironment; recipientKeyId: string; recipientGeneration: number; envelope: plugins.smartcrypto.IX25519EnvelopeV1; } export interface IReq_GetCorestoreControlCredentialMaterial extends plugins.typedrequestInterfaces.implementsTR< plugins.typedrequestInterfaces.ITypedRequest, IReq_GetCorestoreControlCredentialMaterial > { method: 'getCorestoreControlCredentialMaterial'; request: { identity: IIdentityCredential; requestId: string; capability: TCorestoreCredentialCapability; expectedRecipientKeyId: string; expectedRecipientGeneration: number; }; response: { material: ICorestoreControlCredentialMaterialV1; }; } export interface ICorestoreControlCredentialMaterialExpectationV1 { requestId: string; clusterId: string; capability: TCorestoreCredentialCapability; expectedRecipientKeyId: string; expectedRecipientGeneration: number; } export interface ICorestoreControlCredentialMaterialContextInputV1 { requestId: string; clusterId: string; capability: TCorestoreCredentialCapability; recipientKeyId: string; recipientGeneration: number; } const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/; const bareSha256Pattern = /^[a-f0-9]{64}$/; const environmentKeyPattern = /^[A-Z_][A-Z0-9_]{0,127}$/; const isRecord = (valueArg: unknown): valueArg is Record => ( Boolean(valueArg) && typeof valueArg === 'object' && !Array.isArray(valueArg) && (Object.getPrototypeOf(valueArg) === Object.prototype || Object.getPrototypeOf(valueArg) === null) ); const hasExactKeys = ( valueArg: Record, keysArg: readonly string[], ): boolean => { const actual = Object.keys(valueArg).sort(); const expected = [...keysArg].sort(); return actual.length === expected.length && actual.every((keyArg, indexArg) => keyArg === expected[indexArg]); }; const hasKeys = ( valueArg: Record, requiredArg: readonly string[], optionalArg: readonly string[], ): boolean => requiredArg.every((keyArg) => Object.hasOwn(valueArg, keyArg)) && Object.keys(valueArg).every((keyArg) => requiredArg.includes(keyArg) || optionalArg.includes(keyArg)); const isIdentifier = (valueArg: unknown): valueArg is string => ( typeof valueArg === 'string' && identifierPattern.test(valueArg) ); const isPositiveSafeInteger = (valueArg: unknown): valueArg is number => ( Number.isSafeInteger(valueArg) && (valueArg as number) > 0 ); const isNonNegativeSafeInteger = (valueArg: unknown): valueArg is number => ( Number.isSafeInteger(valueArg) && (valueArg as number) >= 0 && !Object.is(valueArg, -0) ); const isJwtIdentity = (valueArg: unknown): valueArg is IIdentityCredential => ( isRecord(valueArg) && hasExactKeys(valueArg, ['jwt']) && typeof valueArg.jwt === 'string' && valueArg.jwt.length > 0 ); const utf8ByteLength = (valueArg: string): number => new TextEncoder().encode(valueArg).byteLength; const isJsonWhitespaceCodePoint = (codePointArg: number): boolean => ( (codePointArg >= 0x0009 && codePointArg <= 0x000d) || codePointArg === 0x0020 || codePointArg === 0x00a0 || codePointArg === 0x1680 || (codePointArg >= 0x2000 && codePointArg <= 0x200a) || codePointArg === 0x2028 || codePointArg === 0x2029 || codePointArg === 0x202f || codePointArg === 0x205f || codePointArg === 0x3000 || codePointArg === 0xfeff ); const isCorestoreControlTokenBytes = (valueArg: Uint8Array): boolean => { if (valueArg.byteLength < corestoreCredentialRuntimeLimits.minimumControlTokenBytes || valueArg.byteLength > corestoreCredentialRuntimeLimits.maximumControlTokenBytes) { return false; } for (let index = 0; index < valueArg.length;) { const first = valueArg[index]!; let codePoint: number; if (first <= 0x7f) { codePoint = first; index++; } else if (first >= 0xc2 && first <= 0xdf && index + 1 < valueArg.length && valueArg[index + 1]! >= 0x80 && valueArg[index + 1]! <= 0xbf) { codePoint = ((first & 0x1f) << 6) | (valueArg[index + 1]! & 0x3f); index += 2; } else if (first >= 0xe0 && first <= 0xef && index + 2 < valueArg.length && valueArg[index + 1]! >= (first === 0xe0 ? 0xa0 : 0x80) && valueArg[index + 1]! <= (first === 0xed ? 0x9f : 0xbf) && valueArg[index + 2]! >= 0x80 && valueArg[index + 2]! <= 0xbf) { codePoint = ((first & 0x0f) << 12) | ((valueArg[index + 1]! & 0x3f) << 6) | (valueArg[index + 2]! & 0x3f); index += 3; } else if (first >= 0xf0 && first <= 0xf4 && index + 3 < valueArg.length && valueArg[index + 1]! >= (first === 0xf0 ? 0x90 : 0x80) && valueArg[index + 1]! <= (first === 0xf4 ? 0x8f : 0xbf) && valueArg[index + 2]! >= 0x80 && valueArg[index + 2]! <= 0xbf && valueArg[index + 3]! >= 0x80 && valueArg[index + 3]! <= 0xbf) { codePoint = ((first & 0x07) << 18) | ((valueArg[index + 1]! & 0x3f) << 12) | ((valueArg[index + 2]! & 0x3f) << 6) | (valueArg[index + 3]! & 0x3f); index += 4; } else { return false; } if (isJsonWhitespaceCodePoint(codePoint)) return false; } return true; }; export const isCorestoreControlToken = (valueArg: unknown): valueArg is string => { if (typeof valueArg !== 'string' || valueArg.length === 0 || /\s/u.test(valueArg)) return false; const encoded = new TextEncoder().encode(valueArg); try { const roundTrip = new TextDecoder('utf-8', { fatal: true }).decode(encoded); return roundTrip === valueArg && encoded.byteLength >= corestoreCredentialRuntimeLimits.minimumControlTokenBytes && encoded.byteLength <= corestoreCredentialRuntimeLimits.maximumControlTokenBytes; } catch { return false; } finally { encoded.fill(0); } }; export const createCorestoreControlCredentialPlaintextBytes = ( tokenBytesArg: Uint8Array, ): Uint8Array | undefined => { if (!isCorestoreControlTokenBytes(tokenBytesArg)) return undefined; const prefix = new TextEncoder().encode( `{"schemaVersion":1,"key":"${corestoreControlCredentialKey}","environment":"${ corestoreCredentialEnvironment }","value":"`, ); const suffix = new Uint8Array([0x22, 0x7d]); const escaped = new Uint8Array(prefix.length + tokenBytesArg.length * 6 + suffix.length); let offset = prefix.length; escaped.set(prefix); for (const byte of tokenBytesArg) { if (byte === 0x22 || byte === 0x5c) { escaped[offset++] = 0x5c; escaped[offset++] = byte; } else if (byte < 0x20) { escaped.set([0x5c, 0x75, 0x30, 0x30], offset); offset += 4; escaped[offset++] = '0123456789abcdef'.charCodeAt(byte >> 4); escaped[offset++] = '0123456789abcdef'.charCodeAt(byte & 0x0f); } else { escaped[offset++] = byte; } } escaped.set(suffix, offset); offset += suffix.length; const plaintext = escaped.slice(0, offset); escaped.fill(0); return plaintext; }; export const validateGetCorestoreControlCredentialMaterialRequest = ( requestArg: unknown, activeRecipientArg: IActiveSecretRecipientMetadata, ): string[] => { if (!isRecord(requestArg) || !hasExactKeys(requestArg, [ 'identity', 'requestId', 'capability', 'expectedRecipientKeyId', 'expectedRecipientGeneration', ])) { return ['corestore control credential request must use its exact schema']; } const errors: string[] = []; if (!isJwtIdentity(requestArg.identity)) { errors.push('corestore control credential request requires only a JWT identity'); } if (!isIdentifier(requestArg.requestId) || !isIdentifier(requestArg.expectedRecipientKeyId)) { errors.push('corestore control credential request identifiers must be canonical'); } if (requestArg.capability !== 'database' && requestArg.capability !== 'objectstorage') { errors.push('corestore control credential capability must be database or objectstorage'); } if (!isPositiveSafeInteger(requestArg.expectedRecipientGeneration)) { errors.push('corestore control credential recipient generation must be positive'); } if (validateSecretRecipientMetadata(activeRecipientArg).length > 0 || activeRecipientArg.lifecycleState !== 'active' || requestArg.expectedRecipientKeyId !== activeRecipientArg.recipientKeyId || requestArg.expectedRecipientGeneration !== activeRecipientArg.generation) { errors.push('corestore control credential request must target the active recipient'); } return errors; }; export const createCorestoreControlCredentialMaterialEnvelopeContext = ( materialArg: ICorestoreControlCredentialMaterialContextInputV1, ): Uint8Array => new TextEncoder().encode(JSON.stringify({ schemaVersion: 1, purpose: 'serve.zone/corestore-control-credential-material', requestId: materialArg.requestId, clusterId: materialArg.clusterId, capability: materialArg.capability, providerConfigId: getCorestoreProviderConfigId(materialArg.capability), controlUrl: corestoreControlUrl, managementScope: corestoreCredentialManagementScope, key: corestoreControlCredentialKey, environment: corestoreCredentialEnvironment, recipientKeyId: materialArg.recipientKeyId, recipientGeneration: materialArg.recipientGeneration, })); export const validateCorestoreControlCredentialPlaintext = ( plaintextArg: unknown, ): string[] => { if (!isRecord(plaintextArg) || !hasExactKeys(plaintextArg, ['schemaVersion', 'key', 'environment', 'value'])) { return ['corestore control credential plaintext must use its exact schema']; } const errors: string[] = []; if (plaintextArg.schemaVersion !== 1 || plaintextArg.key !== corestoreControlCredentialKey || plaintextArg.environment !== corestoreCredentialEnvironment) { errors.push('corestore control credential plaintext constants must be canonical'); } if (!isCorestoreControlToken(plaintextArg.value)) { errors.push('corestore control credential value must be 32-4096 UTF-8 bytes without whitespace'); } return errors; }; export const validateCorestoreControlCredentialMaterial = async ( materialArg: unknown, expectedArg: ICorestoreControlCredentialMaterialExpectationV1, ): Promise => { if (!isRecord(materialArg) || !hasExactKeys(materialArg, [ 'schemaVersion', 'requestId', 'clusterId', 'capability', 'providerConfigId', 'controlUrl', 'managementScope', 'key', 'environment', 'recipientKeyId', 'recipientGeneration', 'envelope', ])) { return ['corestore control credential material must use its exact schema']; } const errors: string[] = []; if (materialArg.schemaVersion !== 1 || !isIdentifier(materialArg.requestId) || !isIdentifier(materialArg.clusterId) || !isIdentifier(materialArg.recipientKeyId) || !isPositiveSafeInteger(materialArg.recipientGeneration)) { errors.push('corestore control credential material metadata must be canonical'); } if (materialArg.capability !== 'database' && materialArg.capability !== 'objectstorage') { errors.push('corestore control credential material capability must be canonical'); } else if (materialArg.providerConfigId !== getCorestoreProviderConfigId(materialArg.capability)) { errors.push('corestore control credential provider must be derived from capability'); } if (materialArg.controlUrl !== corestoreControlUrl || materialArg.managementScope !== corestoreCredentialManagementScope || materialArg.key !== corestoreControlCredentialKey || materialArg.environment !== corestoreCredentialEnvironment) { errors.push('corestore control credential material constants must be canonical'); } if (materialArg.requestId !== expectedArg.requestId || materialArg.clusterId !== expectedArg.clusterId || materialArg.capability !== expectedArg.capability || materialArg.recipientKeyId !== expectedArg.expectedRecipientKeyId || materialArg.recipientGeneration !== expectedArg.expectedRecipientGeneration) { errors.push('corestore control credential material does not match its request authority'); } let envelope: plugins.smartcrypto.IX25519EnvelopeV1 | undefined; try { envelope = plugins.smartcrypto.parseX25519Envelope(materialArg.envelope); if (envelope.ciphertext.length > Math.ceil(corestoreCredentialRuntimeLimits.maximumSealedControlCredentialBytes * 4 / 3)) { errors.push('corestore control credential envelope exceeds its byte limit'); } } catch { errors.push('corestore control credential envelope must be strict'); } if (envelope) { if (envelope.recipientKeyId !== materialArg.recipientKeyId) { errors.push('corestore control credential envelope recipient must match'); } else if (errors.length === 0 && !await verifySecretEnvelopeContext( envelope, createCorestoreControlCredentialMaterialEnvelopeContext( materialArg as unknown as ICorestoreControlCredentialMaterialContextInputV1, ), )) { errors.push('corestore control credential envelope context must match'); } } return errors; }; export interface ICorestoreCredentialPublicationGrantV1 { schemaVersion: 1; operationId: string; serviceId: string; bindingId: string; capability: TCorestoreCredentialCapability; reconciliationGeneration: number; expectedTargetSecretsRevision: number; bindingRequestDigest: string; issuedAt: number; expiresAt: number; } interface ICorestoreCredentialBindingRequestBaseV1 { schemaVersion: 1; serviceId: string; bindingId: string; providerConfigId: typeof corestoreProviderConfigIds[TCorestoreCredentialCapability]; managementScope: typeof corestoreCredentialManagementScope; environment: typeof corestoreCredentialEnvironment; } export type TCorestoreCredentialBindingRequestV1 = | ICorestoreCredentialBindingRequestBaseV1 & { capability: 'database'; } | ICorestoreCredentialBindingRequestBaseV1 & { capability: 'objectstorage'; objectstorageRetention?: IObjectStorageRetentionIntentV1; }; interface ICorestoreCredentialBindingRequestBaseV2 { schemaVersion: 2; serviceId: string; bindingId: string; providerConfigId: typeof corestoreProviderConfigIds[TCorestoreCredentialCapability]; managementScope: typeof corestoreCredentialManagementScope; environment: typeof corestoreCredentialEnvironment; } export type TCorestoreCredentialBindingRequestV2 = | ICorestoreCredentialBindingRequestBaseV2 & { capability: 'database'; } | ICorestoreCredentialBindingRequestBaseV2 & { capability: 'objectstorage'; bucketName: string; objectstorageRetention?: IObjectStorageRetentionIntentV1; }; export const validateCorestoreCredentialBindingRequest = ( requestArg: unknown, ): string[] => { if (!isRecord(requestArg)) { return ['corestore credential binding request must use its exact schema']; } const baseKeys = [ 'schemaVersion', 'serviceId', 'bindingId', 'capability', 'providerConfigId', 'managementScope', 'environment', ]; if (requestArg.capability === 'database') { if (!hasExactKeys(requestArg, baseKeys)) { return ['corestore database credential binding request must use its exact schema']; } } else if (requestArg.capability === 'objectstorage') { if (!hasKeys(requestArg, baseKeys, ['objectstorageRetention'])) { return ['corestore objectstorage credential binding request must use its exact schema']; } } else { return ['corestore credential binding request capability must be canonical']; } const errors: string[] = []; if (requestArg.schemaVersion !== 1 || !isIdentifier(requestArg.serviceId) || !isIdentifier(requestArg.bindingId)) { errors.push('corestore credential binding request identity must be canonical'); } if (requestArg.providerConfigId !== getCorestoreProviderConfigId(requestArg.capability) || requestArg.managementScope !== corestoreCredentialManagementScope || requestArg.environment !== corestoreCredentialEnvironment) { errors.push('corestore credential binding request constants must be canonical'); } if (requestArg.capability === 'objectstorage' && requestArg.objectstorageRetention !== undefined) { errors.push(...validateObjectStorageRetentionIntent( requestArg.objectstorageRetention, 'corestore objectstorage credential binding request retention', )); } return errors; }; const failBindingRequest = (reasonArg: string): never => { throw new Error(reasonArg); }; export const createCorestoreCredentialBindingRequestDigestInput = ( requestArg: TCorestoreCredentialBindingRequestV1, ): string => { const errors = validateCorestoreCredentialBindingRequest(requestArg); if (errors.length > 0) throw new Error(errors[0]); return canonicalizeStrictJson({ schemaVersion: requestArg.schemaVersion, serviceId: requestArg.serviceId, bindingId: requestArg.bindingId, capability: requestArg.capability, providerConfigId: requestArg.providerConfigId, managementScope: requestArg.managementScope, environment: requestArg.environment, ...(requestArg.capability === 'objectstorage' && requestArg.objectstorageRetention ? { objectstorageRetention: { mode: requestArg.objectstorageRetention.mode, policyId: requestArg.objectstorageRetention.policyId, retentionDurationSeconds: requestArg.objectstorageRetention.retentionDurationSeconds, }, } : {}), }, failBindingRequest, 'corestore credential binding request'); }; export const computeCorestoreCredentialBindingRequestSha256 = async ( requestArg: TCorestoreCredentialBindingRequestV1, ): Promise => createCanonicalJsonSha256Hex( createCorestoreCredentialBindingRequestDigestInput(requestArg), failBindingRequest, ); export const validateCorestoreCredentialBindingRequestV2 = ( requestArg: unknown, ): string[] => { if (!isRecord(requestArg)) { return ['corestore credential binding request v2 must use its exact schema']; } const baseKeys = [ 'schemaVersion', 'serviceId', 'bindingId', 'capability', 'providerConfigId', 'managementScope', 'environment', ]; if (requestArg.capability === 'database') { if (!hasExactKeys(requestArg, baseKeys)) { return ['corestore database credential binding request v2 must use its exact schema']; } } else if (requestArg.capability === 'objectstorage') { if (!hasKeys(requestArg, [...baseKeys, 'bucketName'], ['objectstorageRetention'])) { return ['corestore objectstorage credential binding request v2 must use its exact schema']; } } else { return ['corestore credential binding request v2 capability must be canonical']; } const errors: string[] = []; if (requestArg.schemaVersion !== 2 || !isIdentifier(requestArg.serviceId) || !isIdentifier(requestArg.bindingId)) { errors.push('corestore credential binding request v2 identity must be canonical'); } if (requestArg.providerConfigId !== getCorestoreProviderConfigId(requestArg.capability) || requestArg.managementScope !== corestoreCredentialManagementScope || requestArg.environment !== corestoreCredentialEnvironment) { errors.push('corestore credential binding request v2 constants must be canonical'); } if (requestArg.capability === 'objectstorage') { errors.push(...validatePlatformObjectStorageBucketName( requestArg.bucketName, 'corestore objectstorage credential binding request v2 bucketName', )); if (requestArg.objectstorageRetention !== undefined) { errors.push(...validateObjectStorageRetentionIntent( requestArg.objectstorageRetention, 'corestore objectstorage credential binding request v2 retention', )); } } return errors; }; export const createCorestoreCredentialBindingRequestDigestInputV2 = ( requestArg: TCorestoreCredentialBindingRequestV2, ): string => { const errors = validateCorestoreCredentialBindingRequestV2(requestArg); if (errors.length > 0) throw new Error(errors[0]); return canonicalizeStrictJson({ schemaVersion: requestArg.schemaVersion, serviceId: requestArg.serviceId, bindingId: requestArg.bindingId, capability: requestArg.capability, providerConfigId: requestArg.providerConfigId, managementScope: requestArg.managementScope, environment: requestArg.environment, ...(requestArg.capability === 'objectstorage' ? { bucketName: requestArg.bucketName, ...(requestArg.objectstorageRetention ? { objectstorageRetention: { mode: requestArg.objectstorageRetention.mode, policyId: requestArg.objectstorageRetention.policyId, retentionDurationSeconds: requestArg.objectstorageRetention.retentionDurationSeconds, }, } : {}), } : {}), }, failBindingRequest, 'corestore credential binding request v2'); }; export const computeCorestoreCredentialBindingRequestSha256V2 = async ( requestArg: TCorestoreCredentialBindingRequestV2, ): Promise => createCanonicalJsonSha256Hex( createCorestoreCredentialBindingRequestDigestInputV2(requestArg), failBindingRequest, ); export const validateCorestoreCredentialPublicationGrant = ( grantArg: unknown, trustedNowArg?: number, ): string[] => { if (!isRecord(grantArg) || !hasExactKeys(grantArg, [ 'schemaVersion', 'operationId', 'serviceId', 'bindingId', 'capability', 'reconciliationGeneration', 'expectedTargetSecretsRevision', 'bindingRequestDigest', 'issuedAt', 'expiresAt', ])) { return ['corestore credential publication grant must use its exact schema']; } const errors: string[] = []; if (grantArg.schemaVersion !== 1) { errors.push('corestore credential publication grant schemaVersion must be 1'); } if (!isIdentifier(grantArg.operationId) || !isIdentifier(grantArg.serviceId) || !isIdentifier(grantArg.bindingId)) { errors.push('corestore credential publication grant identifiers must be canonical'); } if (grantArg.capability !== 'database' && grantArg.capability !== 'objectstorage') { errors.push('corestore credential publication grant capability must be canonical'); } if (!isPositiveSafeInteger(grantArg.reconciliationGeneration) || !isNonNegativeSafeInteger(grantArg.expectedTargetSecretsRevision)) { errors.push('corestore credential publication grant revisions must be canonical'); } if (typeof grantArg.bindingRequestDigest !== 'string' || !bareSha256Pattern.test(grantArg.bindingRequestDigest)) { errors.push('corestore credential publication grant digest must be bare lowercase 64-hex'); } if (!isPositiveSafeInteger(grantArg.issuedAt) || !isPositiveSafeInteger(grantArg.expiresAt) || (grantArg.expiresAt as number) <= (grantArg.issuedAt as number) || (grantArg.expiresAt as number) - (grantArg.issuedAt as number) > corestoreCredentialRuntimeLimits.maximumGrantLifetimeMs) { errors.push('corestore credential publication grant lifetime must be positive and bounded'); } if (trustedNowArg !== undefined && (!isPositiveSafeInteger(trustedNowArg) || trustedNowArg < (grantArg.issuedAt as number) || trustedNowArg >= (grantArg.expiresAt as number))) { errors.push('corestore credential publication grant must be currently valid'); } return errors; }; export const corestoreCredentialPublicationGrantsEqual = ( leftArg: unknown, rightArg: unknown, ): boolean => validateCorestoreCredentialPublicationGrant(leftArg).length === 0 && validateCorestoreCredentialPublicationGrant(rightArg).length === 0 && (leftArg as ICorestoreCredentialPublicationGrantV1).schemaVersion === (rightArg as ICorestoreCredentialPublicationGrantV1).schemaVersion && (leftArg as ICorestoreCredentialPublicationGrantV1).operationId === (rightArg as ICorestoreCredentialPublicationGrantV1).operationId && (leftArg as ICorestoreCredentialPublicationGrantV1).serviceId === (rightArg as ICorestoreCredentialPublicationGrantV1).serviceId && (leftArg as ICorestoreCredentialPublicationGrantV1).bindingId === (rightArg as ICorestoreCredentialPublicationGrantV1).bindingId && (leftArg as ICorestoreCredentialPublicationGrantV1).capability === (rightArg as ICorestoreCredentialPublicationGrantV1).capability && (leftArg as ICorestoreCredentialPublicationGrantV1).reconciliationGeneration === (rightArg as ICorestoreCredentialPublicationGrantV1).reconciliationGeneration && (leftArg as ICorestoreCredentialPublicationGrantV1).expectedTargetSecretsRevision === (rightArg as ICorestoreCredentialPublicationGrantV1).expectedTargetSecretsRevision && (leftArg as ICorestoreCredentialPublicationGrantV1).bindingRequestDigest === (rightArg as ICorestoreCredentialPublicationGrantV1).bindingRequestDigest && (leftArg as ICorestoreCredentialPublicationGrantV1).issuedAt === (rightArg as ICorestoreCredentialPublicationGrantV1).issuedAt && (leftArg as ICorestoreCredentialPublicationGrantV1).expiresAt === (rightArg as ICorestoreCredentialPublicationGrantV1).expiresAt; const comparePublicationGrants = ( leftArg: ICorestoreCredentialPublicationGrantV1, rightArg: ICorestoreCredentialPublicationGrantV1, ): number => leftArg.bindingId < rightArg.bindingId ? -1 : leftArg.bindingId > rightArg.bindingId ? 1 : leftArg.operationId < rightArg.operationId ? -1 : leftArg.operationId > rightArg.operationId ? 1 : 0; export const validateCorestoreCredentialPublicationGrants = ( grantsArg: unknown, trustedNowArg?: number, ): string[] => { if (!Array.isArray(grantsArg) || grantsArg.length > corestoreCredentialRuntimeLimits.maximumPublicationGrants) { return ['corestore credential publication grants must be a bounded array']; } const errors: string[] = []; let previous: ICorestoreCredentialPublicationGrantV1 | undefined; for (const [index, grantArg] of grantsArg.entries()) { const grantErrors = validateCorestoreCredentialPublicationGrant(grantArg, trustedNowArg); errors.push(...grantErrors.map((errorArg) => `publication grants[${index}] ${errorArg}`)); if (grantErrors.length === 0) { const grant = grantArg as ICorestoreCredentialPublicationGrantV1; if (previous && comparePublicationGrants(previous, grant) >= 0) { errors.push('corestore credential publication grants must be unique and sorted by bindingId then operationId'); } previous = grant; } } return errors; }; export const corestoreDatabaseCredentialKeys = Object.freeze([ 'MONGODB_URI', 'MONGODB_URL', 'MONGO_URL', 'MONGODB_HOST', 'MONGODB_PORT', 'MONGODB_DATABASE', 'MONGO_DBNAME', 'MONGODB_USERNAME', 'MONGO_DBUSER', 'MONGODB_PASSWORD', 'MONGO_DBPASS', ] as const); export const corestoreObjectStorageCredentialPlaintextKeys = Object.freeze([ 'accessKeyId', 'secretAccessKey', ] as const); export const corestoreObjectStorageCredentialSecretKeys = Object.freeze([ 'S3_ACCESS_KEY', 'S3_ACCESS_KEY_ID', 'S3_SECRET_KEY', 'S3_SECRET_ACCESS_KEY', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', ] as const); export type TCorestoreDatabaseCredentialKey = typeof corestoreDatabaseCredentialKeys[number]; export type TCorestoreObjectStorageCredentialPlaintextKey = typeof corestoreObjectStorageCredentialPlaintextKeys[number]; export type TCorestoreObjectStorageCredentialSecretKey = typeof corestoreObjectStorageCredentialSecretKeys[number]; export type TCorestoreCredentialPlaintextKey = | TCorestoreDatabaseCredentialKey | TCorestoreObjectStorageCredentialPlaintextKey; export type TCorestoreCredentialSecretKey = | TCorestoreDatabaseCredentialKey | TCorestoreObjectStorageCredentialSecretKey; export type TCorestoreDatabaseCredentialValuesV1 = { [TKey in TCorestoreDatabaseCredentialKey]: string; }; export interface ICorestoreDatabaseCredentialPlaintextMaterialV1 { schemaVersion: 1; capability: 'database'; values: TCorestoreDatabaseCredentialValuesV1; } export interface ICorestoreObjectStorageCredentialPlaintextMaterialV1 { schemaVersion: 1; capability: 'objectstorage'; values: { accessKeyId: string; secretAccessKey: string; }; retention?: IObjectStorageRetentionEvidenceV1; } export type TCorestoreCredentialPlaintextMaterialV1 = | ICorestoreDatabaseCredentialPlaintextMaterialV1 | ICorestoreObjectStorageCredentialPlaintextMaterialV1; const readCredentialString = (valueArg: unknown): boolean => typeof valueArg === 'string' && valueArg.length > 0 && utf8ByteLength(valueArg) <= corestoreCredentialRuntimeLimits.maximumCredentialStringBytes && !valueArg.includes('\0'); export const validateCorestoreDatabaseCredentialMaterial = ( materialArg: unknown, ): string[] => { if (!isRecord(materialArg) || !hasExactKeys(materialArg, ['schemaVersion', 'capability', 'values']) || materialArg.schemaVersion !== 1 || materialArg.capability !== 'database' || !isRecord(materialArg.values) || !hasExactKeys(materialArg.values, corestoreDatabaseCredentialKeys)) { return ['corestore database credential material must use its exact schema']; } const values = materialArg.values; const errors: string[] = []; if (corestoreDatabaseCredentialKeys.some((keyArg) => !readCredentialString(values[keyArg]))) { errors.push('corestore database credential values must be bounded non-empty strings'); } if (values.MONGODB_URI !== values.MONGODB_URL || values.MONGODB_URI !== values.MONGO_URL) { errors.push('corestore database URI aliases must be equal'); } if (values.MONGODB_DATABASE !== values.MONGO_DBNAME) { errors.push('corestore database name aliases must be equal'); } if (values.MONGODB_USERNAME !== values.MONGO_DBUSER) { errors.push('corestore database username aliases must be equal'); } if (values.MONGODB_PASSWORD !== values.MONGO_DBPASS) { errors.push('corestore database password aliases must be equal'); } if (typeof values.MONGODB_PORT !== 'string' || !/^[1-9][0-9]{0,4}$/.test(values.MONGODB_PORT) || Number(values.MONGODB_PORT) > 65535) { errors.push('corestore database port must be canonical'); } return errors; }; export const validateCorestoreObjectStorageCredentialMaterial = async ( materialArg: unknown, retentionExpectationArg?: IObjectStorageRetentionEvidenceExpectationV1, ): Promise => { if (!isRecord(materialArg) || !hasKeys(materialArg, ['schemaVersion', 'capability', 'values'], ['retention']) || materialArg.schemaVersion !== 1 || materialArg.capability !== 'objectstorage' || !isRecord(materialArg.values) || !hasExactKeys(materialArg.values, corestoreObjectStorageCredentialPlaintextKeys)) { return ['corestore objectstorage credential material must use its exact schema']; } const errors: string[] = []; if (!readCredentialString(materialArg.values.accessKeyId) || !readCredentialString(materialArg.values.secretAccessKey)) { errors.push('corestore objectstorage credential values must be bounded non-empty strings'); } if (retentionExpectationArg && materialArg.retention === undefined) { errors.push('corestore objectstorage retention evidence is required by trusted binding authority'); } else if (materialArg.retention !== undefined) { if (!retentionExpectationArg) { errors.push('corestore objectstorage retention evidence requires trusted binding authority'); } else { errors.push(...await validateObjectStorageRetentionEvidence( materialArg.retention, retentionExpectationArg, )); } } return errors; }; export const validateCorestoreCredentialPlaintextMaterial = async ( materialArg: unknown, retentionExpectationArg?: IObjectStorageRetentionEvidenceExpectationV1, ): Promise => isRecord(materialArg) && materialArg.capability === 'database' ? validateCorestoreDatabaseCredentialMaterial(materialArg) : validateCorestoreObjectStorageCredentialMaterial(materialArg, retentionExpectationArg); export interface ICorestoreCredentialSecretValueV1 { key: TCorestoreCredentialSecretKey; value: string; } export const createCorestoreCredentialSecretValues = async ( materialArg: TCorestoreCredentialPlaintextMaterialV1, retentionExpectationArg?: IObjectStorageRetentionEvidenceExpectationV1, ): Promise => { const errors = await validateCorestoreCredentialPlaintextMaterial( materialArg, retentionExpectationArg, ); if (errors.length > 0) throw new Error(errors[0]); if (materialArg.capability === 'database') { return corestoreDatabaseCredentialKeys.map((keyArg) => ({ key: keyArg, value: materialArg.values[keyArg], })); } return corestoreObjectStorageCredentialSecretKeys.map((keyArg) => ({ key: keyArg, value: keyArg === 'S3_ACCESS_KEY' || keyArg === 'S3_ACCESS_KEY_ID' || keyArg === 'AWS_ACCESS_KEY_ID' ? materialArg.values.accessKeyId : materialArg.values.secretAccessKey, })); }; export interface IPublishCorestoreCredentialMaterialRequestV1 { identity: IIdentityCredential; mutationId: string; grant: ICorestoreCredentialPublicationGrantV1; expectedIngressRecipientKeyId: string; expectedIngressRecipientGeneration: number; envelope: plugins.smartcrypto.IX25519EnvelopeV1; } export interface ICorestoreCredentialSecretVersionReferenceV1 extends ISecretVersionReference { key: TCorestoreCredentialSecretKey; } export interface ICorestoreCredentialPublicationCoverageV1 { keys: TCorestoreCredentialSecretKey[]; versionReferences: ICorestoreCredentialSecretVersionReferenceV1[]; } export interface ICorestoreCredentialPublicationReceiptV1 { schemaVersion: 1; mutationId: string; organizationId: string; clusterId: string; serviceId: string; bindingId: string; capability: TCorestoreCredentialCapability; grant: ICorestoreCredentialPublicationGrantV1; coverage: ICorestoreCredentialPublicationCoverageV1; expectedTargetSecretsRevision: number; targetSecretsRevision: number; ingressAdmissionBinding: ISecretEnvelopeAdmissionBindingV1; acceptedAt: number; replayed: boolean; } export interface IReq_PublishCorestoreCredentialMaterial extends plugins.typedrequestInterfaces.implementsTR< plugins.typedrequestInterfaces.ITypedRequest, IReq_PublishCorestoreCredentialMaterial > { method: 'publishCorestoreCredentialMaterial'; request: IPublishCorestoreCredentialMaterialRequestV1; response: { receipt: ICorestoreCredentialPublicationReceiptV1; }; } export const createPublishCorestoreCredentialMaterialEnvelopeContext = ( requestArg: Omit, ): Uint8Array => new TextEncoder().encode(JSON.stringify({ schemaVersion: 1, purpose: 'serve.zone/publish-corestore-credential-material', mutationId: requestArg.mutationId, grant: { schemaVersion: requestArg.grant.schemaVersion, operationId: requestArg.grant.operationId, serviceId: requestArg.grant.serviceId, bindingId: requestArg.grant.bindingId, capability: requestArg.grant.capability, reconciliationGeneration: requestArg.grant.reconciliationGeneration, expectedTargetSecretsRevision: requestArg.grant.expectedTargetSecretsRevision, bindingRequestDigest: requestArg.grant.bindingRequestDigest, issuedAt: requestArg.grant.issuedAt, expiresAt: requestArg.grant.expiresAt, }, expectedIngressRecipientKeyId: requestArg.expectedIngressRecipientKeyId, expectedIngressRecipientGeneration: requestArg.expectedIngressRecipientGeneration, })); export const validatePublishCorestoreCredentialMaterialRequest = async ( requestArg: unknown, expectedGrantArg: ICorestoreCredentialPublicationGrantV1, activeRecipientArg: IActiveSecretRecipientMetadata, trustedNowArg: number, ): Promise => { if (!isRecord(requestArg) || !hasExactKeys(requestArg, [ 'identity', 'mutationId', 'grant', 'expectedIngressRecipientKeyId', 'expectedIngressRecipientGeneration', 'envelope', ])) { return ['publish corestore credential request must use its exact schema']; } const errors = validateCorestoreCredentialPublicationGrant(requestArg.grant, trustedNowArg); if (!corestoreCredentialPublicationGrantsEqual(requestArg.grant, expectedGrantArg)) { errors.push('publish corestore credential request grant must match current server authority'); } if (!isJwtIdentity(requestArg.identity)) { errors.push('publish corestore credential request requires only a JWT identity'); } if (!isIdentifier(requestArg.mutationId) || !isIdentifier(requestArg.expectedIngressRecipientKeyId) || !isPositiveSafeInteger(requestArg.expectedIngressRecipientGeneration)) { errors.push('publish corestore credential request recipient and mutation must be canonical'); } let envelope: plugins.smartcrypto.IX25519EnvelopeV1 | undefined; try { envelope = plugins.smartcrypto.parseX25519Envelope(requestArg.envelope); if (envelope.ciphertext.length > Math.ceil(corestoreCredentialRuntimeLimits.maximumSealedMaterialBytes * 4 / 3)) { errors.push('publish corestore credential envelope exceeds its byte limit'); } } catch { errors.push('publish corestore credential envelope must be strict'); } if (validateSecretRecipientMetadata(activeRecipientArg).length > 0 || activeRecipientArg.lifecycleState !== 'active' || activeRecipientArg.recipientKeyId !== requestArg.expectedIngressRecipientKeyId || activeRecipientArg.generation !== requestArg.expectedIngressRecipientGeneration) { errors.push('publish corestore credential request must target the active ingress recipient'); } if (envelope) { if (envelope.recipientKeyId !== requestArg.expectedIngressRecipientKeyId) { errors.push('publish corestore credential envelope recipient must match'); } else if (errors.length === 0 && !await verifySecretEnvelopeContext( envelope, createPublishCorestoreCredentialMaterialEnvelopeContext( requestArg as unknown as Omit< IPublishCorestoreCredentialMaterialRequestV1, 'identity' | 'envelope' >, ), )) { errors.push('publish corestore credential envelope context must match'); } } return errors; }; const expectedCoverageKeys = ( capabilityArg: TCorestoreCredentialCapability, ): readonly TCorestoreCredentialSecretKey[] => capabilityArg === 'database' ? corestoreDatabaseCredentialKeys : corestoreObjectStorageCredentialSecretKeys; export interface ICorestoreCredentialPublicationReceiptExpectationV1 { request: IPublishCorestoreCredentialMaterialRequestV1; organizationId: string; clusterId: string; } const computeRuntimeSha256 = async (valueArg: Uint8Array): Promise => { const digest = new Uint8Array(await globalThis.crypto.subtle.digest( 'SHA-256', new Uint8Array(valueArg), )); return [...digest].map((byteArg) => byteArg.toString(16).padStart(2, '0')).join(''); }; const verifyPublicationAdmissionBinding = async ( bindingArg: ISecretEnvelopeAdmissionBindingV1, envelopeArg: unknown, contextArg: Uint8Array, ): Promise => { try { const envelope = plugins.smartcrypto.parseX25519Envelope(envelopeArg); if (envelope.recipientKeyId !== bindingArg.recipientKeyId || !await verifySecretEnvelopeContext(envelope, contextArg)) { return false; } const envelopeDigestInput = JSON.stringify({ schemaVersion: envelope.schemaVersion, profile: envelope.profile, recipientKeyId: envelope.recipientKeyId, ephemeralPublicKey: envelope.ephemeralPublicKey, nonce: envelope.nonce, ciphertext: envelope.ciphertext, tag: envelope.tag, contextDigest: envelope.contextDigest, }); return bindingArg.envelopeDigest === `sha256:${await computeRuntimeSha256(new TextEncoder().encode(envelopeDigestInput))}` && bindingArg.requestContextDigest === `sha256:${await computeRuntimeSha256(contextArg)}`; } catch { return false; } }; export const validateCorestoreCredentialPublicationReceipt = async ( receiptArg: unknown, expectedArg: ICorestoreCredentialPublicationReceiptExpectationV1, ): Promise => { if (!isRecord(receiptArg) || !hasExactKeys(receiptArg, [ 'schemaVersion', 'mutationId', 'organizationId', 'clusterId', 'serviceId', 'bindingId', 'capability', 'grant', 'coverage', 'expectedTargetSecretsRevision', 'targetSecretsRevision', 'ingressAdmissionBinding', 'acceptedAt', 'replayed', ])) { return ['corestore credential publication receipt must use its exact schema']; } const errors = validateCorestoreCredentialPublicationGrant(receiptArg.grant); if (receiptArg.schemaVersion !== 1 || !isIdentifier(receiptArg.mutationId) || !isIdentifier(receiptArg.organizationId) || !isIdentifier(receiptArg.clusterId) || !isIdentifier(receiptArg.serviceId) || !isIdentifier(receiptArg.bindingId)) { errors.push('corestore credential publication receipt identifiers must be canonical'); } const grant = receiptArg.grant as ICorestoreCredentialPublicationGrantV1; if ((receiptArg.capability !== 'database' && receiptArg.capability !== 'objectstorage') || receiptArg.serviceId !== grant.serviceId || receiptArg.bindingId !== grant.bindingId || receiptArg.capability !== grant.capability || receiptArg.expectedTargetSecretsRevision !== grant.expectedTargetSecretsRevision) { errors.push('corestore credential publication receipt must exactly bind its grant'); } if (receiptArg.mutationId !== expectedArg.request.mutationId || receiptArg.organizationId !== expectedArg.organizationId || receiptArg.clusterId !== expectedArg.clusterId || !corestoreCredentialPublicationGrantsEqual(receiptArg.grant, expectedArg.request.grant)) { errors.push('corestore credential publication receipt must match its trusted request scope'); } if (!isRecord(receiptArg.coverage) || !hasExactKeys(receiptArg.coverage, ['keys', 'versionReferences']) || !Array.isArray(receiptArg.coverage.keys) || !Array.isArray(receiptArg.coverage.versionReferences)) { errors.push('corestore credential publication coverage must use its exact schema'); } else if (receiptArg.capability === 'database' || receiptArg.capability === 'objectstorage') { const requiredKeys = expectedCoverageKeys(receiptArg.capability); if (JSON.stringify(receiptArg.coverage.keys) !== JSON.stringify(requiredKeys) || receiptArg.coverage.versionReferences.length !== requiredKeys.length) { errors.push('corestore credential publication coverage must be exact'); } else { const secretIds = new Set(); const secretVersionIds = new Set(); for (const [index, referenceArg] of receiptArg.coverage.versionReferences.entries()) { if (!isRecord(referenceArg) || !hasExactKeys(referenceArg, ['key', 'secretId', 'secretVersionId']) || referenceArg.key !== requiredKeys[index] || !isIdentifier(referenceArg.secretId) || !isIdentifier(referenceArg.secretVersionId)) { errors.push('corestore credential publication version references must exactly cover keys'); break; } if (secretIds.has(referenceArg.secretId as string) || secretVersionIds.has(referenceArg.secretVersionId as string)) { errors.push('corestore credential publication version references must be unique'); break; } secretIds.add(referenceArg.secretId as string); secretVersionIds.add(referenceArg.secretVersionId as string); } } } const referenceCount = isRecord(receiptArg.coverage) && Array.isArray(receiptArg.coverage.versionReferences) ? receiptArg.coverage.versionReferences.length : 0; if (!isNonNegativeSafeInteger(receiptArg.expectedTargetSecretsRevision) || !isNonNegativeSafeInteger(receiptArg.targetSecretsRevision) || receiptArg.targetSecretsRevision !== receiptArg.expectedTargetSecretsRevision + referenceCount) { errors.push('corestore credential publication target Secrets revision must exactly cover created versions'); } const admission = receiptArg.ingressAdmissionBinding; if (!isRecord(admission) || !hasExactKeys(admission, [ 'schemaVersion', 'recipientKeyId', 'recipientGeneration', 'envelopeDigest', 'requestContextDigest', ]) || admission.schemaVersion !== 1 || !isIdentifier(admission.recipientKeyId) || !isPositiveSafeInteger(admission.recipientGeneration) || typeof admission.envelopeDigest !== 'string' || !isSha256Digest(admission.envelopeDigest) || typeof admission.requestContextDigest !== 'string' || !isSha256Digest(admission.requestContextDigest)) { errors.push('corestore credential publication ingress admission binding must be canonical'); } else if (admission.recipientKeyId !== expectedArg.request.expectedIngressRecipientKeyId || admission.recipientGeneration !== expectedArg.request.expectedIngressRecipientGeneration || !await verifyPublicationAdmissionBinding( admission as unknown as ISecretEnvelopeAdmissionBindingV1, expectedArg.request.envelope, createPublishCorestoreCredentialMaterialEnvelopeContext(expectedArg.request), )) { errors.push('corestore credential publication ingress admission binding must match the request'); } if (!isPositiveSafeInteger(receiptArg.acceptedAt) || receiptArg.acceptedAt < grant.issuedAt || receiptArg.acceptedAt >= grant.expiresAt || typeof receiptArg.replayed !== 'boolean') { errors.push('corestore credential publication receipt acceptance must be canonical'); } return errors; }; export const corestoreCredentialPublicationErrorMetadata = Object.freeze({ INVALID_IDENTITY: Object.freeze({ retryable: false }), INVALID_REQUEST: Object.freeze({ retryable: false }), GRANT_EXPIRED: Object.freeze({ retryable: false }), GRANT_SCOPE_MISMATCH: Object.freeze({ retryable: false }), GRANT_REVISION_MISMATCH: Object.freeze({ retryable: true }), INGRESS_RECIPIENT_MISMATCH: Object.freeze({ retryable: true }), ENVELOPE_INVALID: Object.freeze({ retryable: false }), MATERIAL_INVALID: Object.freeze({ retryable: false }), SECRETS_REVISION_CONFLICT: Object.freeze({ retryable: true }), REPLAY_CONFLICT: Object.freeze({ retryable: false }), INTERNAL_ERROR: Object.freeze({ retryable: true }), } as const); export type TCorestoreCredentialPublicationErrorCode = keyof typeof corestoreCredentialPublicationErrorMetadata; export interface ICorestoreCredentialPublicationErrorMetadataV1 { schemaVersion: 1; code: TCorestoreCredentialPublicationErrorCode; retryable: boolean; } export const getCorestoreCredentialPublicationErrorMetadata = ( codeArg: TCorestoreCredentialPublicationErrorCode, ): ICorestoreCredentialPublicationErrorMetadataV1 => ({ schemaVersion: 1, code: codeArg, retryable: corestoreCredentialPublicationErrorMetadata[codeArg].retryable, }); const corestoreDatabaseBackupMaximumSnapshotPayloadBytes = 96 * 1024 * 1024; export const corestoreDatabaseBackupRuntimeLimits = Object.freeze({ maximumControlBytes: 16 * 1024, maximumClosureBytes: 256 * 1024 * 1024, maximumVerifiedPlaintextBytes: 128 * 1024 * 1024, maximumSnapshotPayloadBytes: corestoreDatabaseBackupMaximumSnapshotPayloadBytes, maximumSnapshotOriginalBytes: corestoreDatabaseBackupMaximumSnapshotPayloadBytes + 1, maximumClosureObjects: 4096, maximumClosureChunks: 1_000_000, maximumServiceNameBytes: 1024, }); export interface ICorestoreDatabaseAllocationReference { purpose: 'scratch-rehearsal'; rehearsalId: string; allocationId: string; generation: number; bindingSha256: string; receiptSha256: string; } export interface ICorestoreDatabaseBackupSnapshotTags { corestore: 'resource'; serviceId: string; serviceName: string; capability: 'database'; resourceName: string; snapshotName: string; backupId: string; } export interface ICorestoreDatabaseBackupSnapshot { capability: 'database'; resourceName: string; snapshotId: string; snapshotName: string; originalSize: number; storedSize: number; createdAt: number; tags: ICorestoreDatabaseBackupSnapshotTags; databaseName: string; databaseAllocation?: ICorestoreDatabaseAllocationReference; } export interface ICorestoreDatabaseBackupClosureReceipt { formatVersion: 1; snapshotIds: [string]; objectCount: number; packCount: number; chunkCount: number; payloadBytes: number; archiveBytes: number; sha256: string; } export interface ICorestoreDatabaseBackupReceipt { schemaVersion: 1; format: 'corestore.database.backup.v1'; backupId: string; sourceServiceId: string; createdAt: number; snapshot: ICorestoreDatabaseBackupSnapshot; closure: ICorestoreDatabaseBackupClosureReceipt; } export interface ICorestoreDatabaseBackupRestoreRequest { schemaVersion: 1; targetServiceId: string; receipt: ICorestoreDatabaseBackupReceipt; expectedDatabaseAllocation?: ICorestoreDatabaseAllocationReference; /** Caller-owned idempotency identity. A new value forces a new fenced restore attempt. */ restoreAttemptId?: string; } export interface ICorestoreDatabaseBackupRestoreResponse { schemaVersion: 1; ok: true; backupId: string; sourceServiceId: string; targetServiceId: string; snapshotId: string; restoreId: string; requestSha256: string; replayed: boolean; } const corestoreDatabaseBackupIdentifierPattern = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/; const corestoreDatabaseAllocationRehearsalIdPattern = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,191}$/; const failCorestoreDatabaseBackup = (reasonArg: string): never => { throw new Error(reasonArg); }; const readCorestoreDatabaseBackupRecord = ( valueArg: unknown, pathArg: string, ): Record => { if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) { return failCorestoreDatabaseBackup(`${pathArg} must be a plain object`); } const prototype = Object.getPrototypeOf(valueArg); if (prototype !== Object.prototype && prototype !== null) { return failCorestoreDatabaseBackup(`${pathArg} must be a plain object`); } const value = valueArg as Record; for (const key of Reflect.ownKeys(value)) { if (typeof key !== 'string') { return failCorestoreDatabaseBackup(`${pathArg} must contain string keys only`); } const descriptor = Object.getOwnPropertyDescriptor(value, key); if (!descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, 'value')) { return failCorestoreDatabaseBackup(`${pathArg} must contain data properties only`); } } return value; }; const assertCorestoreDatabaseBackupExactKeys = ( valueArg: Record, keysArg: readonly string[], pathArg: string, ): void => { if (!hasExactKeys(valueArg, keysArg)) { failCorestoreDatabaseBackup(`${pathArg} must use its exact schema`); } }; const readCorestoreDatabaseBackupIdentifier = ( valueArg: unknown, pathArg: string, ): string => { if (typeof valueArg !== 'string' || !corestoreDatabaseBackupIdentifierPattern.test(valueArg)) { return failCorestoreDatabaseBackup(`${pathArg} must be a canonical identifier`); } return valueArg; }; const readCorestoreDatabaseBackupSha256 = ( valueArg: unknown, pathArg: string, ): string => { if (typeof valueArg !== 'string' || !bareSha256Pattern.test(valueArg)) { return failCorestoreDatabaseBackup(`${pathArg} must be bare lowercase SHA-256`); } return valueArg; }; const readCorestoreDatabaseBackupInteger = ( valueArg: unknown, pathArg: string, minimumArg: number, maximumArg = Number.MAX_SAFE_INTEGER, ): number => { if (!Number.isSafeInteger(valueArg) || Object.is(valueArg, -0) || (valueArg as number) < minimumArg || (valueArg as number) > maximumArg) { return failCorestoreDatabaseBackup(`${pathArg} must be a bounded safe integer`); } return valueArg as number; }; const finalizeCorestoreDatabaseBackup = ( valueArg: T, pathArg: string, ): Readonly => { const canonical = canonicalizeStrictJson(valueArg, failCorestoreDatabaseBackup, pathArg); if (utf8ByteLength(canonical) > corestoreDatabaseBackupRuntimeLimits.maximumControlBytes) { failCorestoreDatabaseBackup(`${pathArg} exceeds its canonical byte limit`); } return deepFreezeValue(valueArg); }; const normalizeCorestoreDatabaseAllocationReferenceInternal = ( valueArg: unknown, pathArg: string, ): ICorestoreDatabaseAllocationReference => { const value = readCorestoreDatabaseBackupRecord(valueArg, pathArg); assertCorestoreDatabaseBackupExactKeys(value, [ 'purpose', 'rehearsalId', 'allocationId', 'generation', 'bindingSha256', 'receiptSha256', ], pathArg); if (value.purpose !== 'scratch-rehearsal' || typeof value.rehearsalId !== 'string' || !corestoreDatabaseAllocationRehearsalIdPattern.test(value.rehearsalId)) { failCorestoreDatabaseBackup(`${pathArg} rehearsal authority must be canonical`); } const rehearsalId = value.rehearsalId as string; return { purpose: 'scratch-rehearsal', rehearsalId, allocationId: readCorestoreDatabaseBackupSha256( value.allocationId, `${pathArg}.allocationId`, ), generation: readCorestoreDatabaseBackupInteger( value.generation, `${pathArg}.generation`, 1, ), bindingSha256: readCorestoreDatabaseBackupSha256( value.bindingSha256, `${pathArg}.bindingSha256`, ), receiptSha256: readCorestoreDatabaseBackupSha256( value.receiptSha256, `${pathArg}.receiptSha256`, ), }; }; export const normalizeCorestoreDatabaseAllocationReference = ( valueArg: unknown, ): Readonly => finalizeCorestoreDatabaseBackup( normalizeCorestoreDatabaseAllocationReferenceInternal(valueArg, 'databaseAllocation'), 'databaseAllocation', ); const normalizeCorestoreDatabaseBackupReceiptInternal = ( valueArg: unknown, pathArg: string, ): ICorestoreDatabaseBackupReceipt => { const value = readCorestoreDatabaseBackupRecord(valueArg, pathArg); assertCorestoreDatabaseBackupExactKeys(value, [ 'schemaVersion', 'format', 'backupId', 'sourceServiceId', 'createdAt', 'snapshot', 'closure', ], pathArg); if (value.schemaVersion !== 1 || value.format !== 'corestore.database.backup.v1') { failCorestoreDatabaseBackup(`${pathArg} version must be canonical`); } const backupId = readCorestoreDatabaseBackupIdentifier(value.backupId, `${pathArg}.backupId`); const sourceServiceId = readCorestoreDatabaseBackupIdentifier( value.sourceServiceId, `${pathArg}.sourceServiceId`, ); const createdAt = readCorestoreDatabaseBackupInteger(value.createdAt, `${pathArg}.createdAt`, 1); const snapshotValue = readCorestoreDatabaseBackupRecord(value.snapshot, `${pathArg}.snapshot`); const hasDatabaseAllocation = Object.hasOwn(snapshotValue, 'databaseAllocation'); assertCorestoreDatabaseBackupExactKeys(snapshotValue, [ 'capability', 'resourceName', 'snapshotId', 'snapshotName', 'originalSize', 'storedSize', 'createdAt', 'tags', 'databaseName', ...(hasDatabaseAllocation ? ['databaseAllocation'] : []), ], `${pathArg}.snapshot`); if (snapshotValue.capability !== 'database') { failCorestoreDatabaseBackup(`${pathArg}.snapshot capability must be database`); } const resourceName = readCorestoreDatabaseBackupIdentifier( snapshotValue.resourceName, `${pathArg}.snapshot.resourceName`, ); const snapshotId = readCorestoreDatabaseBackupIdentifier( snapshotValue.snapshotId, `${pathArg}.snapshot.snapshotId`, ); const snapshotName = readCorestoreDatabaseBackupIdentifier( snapshotValue.snapshotName, `${pathArg}.snapshot.snapshotName`, ); const databaseName = readCorestoreDatabaseBackupIdentifier( snapshotValue.databaseName, `${pathArg}.snapshot.databaseName`, ); const snapshotCreatedAt = readCorestoreDatabaseBackupInteger( snapshotValue.createdAt, `${pathArg}.snapshot.createdAt`, 1, ); if (snapshotName !== backupId || snapshotCreatedAt !== createdAt) { failCorestoreDatabaseBackup(`${pathArg}.snapshot identity must match its receipt`); } const tagsValue = readCorestoreDatabaseBackupRecord( snapshotValue.tags, `${pathArg}.snapshot.tags`, ); assertCorestoreDatabaseBackupExactKeys(tagsValue, [ 'corestore', 'serviceId', 'serviceName', 'capability', 'resourceName', 'snapshotName', 'backupId', ], `${pathArg}.snapshot.tags`); if (tagsValue.corestore !== 'resource' || tagsValue.serviceId !== sourceServiceId || tagsValue.capability !== 'database' || tagsValue.resourceName !== resourceName || tagsValue.snapshotName !== backupId || tagsValue.backupId !== backupId || typeof tagsValue.serviceName !== 'string' || tagsValue.serviceName.length > 256 || utf8ByteLength(tagsValue.serviceName) > corestoreDatabaseBackupRuntimeLimits.maximumServiceNameBytes) { failCorestoreDatabaseBackup(`${pathArg}.snapshot.tags ownership must be canonical`); } const serviceName = tagsValue.serviceName as string; const closureValue = readCorestoreDatabaseBackupRecord(value.closure, `${pathArg}.closure`); assertCorestoreDatabaseBackupExactKeys(closureValue, [ 'formatVersion', 'snapshotIds', 'objectCount', 'packCount', 'chunkCount', 'payloadBytes', 'archiveBytes', 'sha256', ], `${pathArg}.closure`); if (!Array.isArray(closureValue.snapshotIds) || Object.getPrototypeOf(closureValue.snapshotIds) !== Array.prototype || closureValue.snapshotIds.length !== 1 || !Object.hasOwn(closureValue.snapshotIds, 0) || Reflect.ownKeys(closureValue.snapshotIds).some((keyArg) => keyArg !== 'length' && keyArg !== '0')) { failCorestoreDatabaseBackup(`${pathArg}.closure snapshot coverage must be exact`); } const snapshotIdDescriptor = Object.getOwnPropertyDescriptor(closureValue.snapshotIds, '0'); if (!snapshotIdDescriptor || !snapshotIdDescriptor.enumerable || !Object.hasOwn(snapshotIdDescriptor, 'value') || snapshotIdDescriptor.value !== snapshotId) { failCorestoreDatabaseBackup(`${pathArg}.closure snapshot coverage must use data entries`); } const objectCount = readCorestoreDatabaseBackupInteger( closureValue.objectCount, `${pathArg}.closure.objectCount`, 1, corestoreDatabaseBackupRuntimeLimits.maximumClosureObjects, ); const packCount = readCorestoreDatabaseBackupInteger( closureValue.packCount, `${pathArg}.closure.packCount`, 1, corestoreDatabaseBackupRuntimeLimits.maximumClosureObjects, ); const chunkCount = readCorestoreDatabaseBackupInteger( closureValue.chunkCount, `${pathArg}.closure.chunkCount`, 1, corestoreDatabaseBackupRuntimeLimits.maximumClosureChunks, ); if (packCount > objectCount) { failCorestoreDatabaseBackup(`${pathArg}.closure pack coverage must be canonical`); } const originalSize = readCorestoreDatabaseBackupInteger( snapshotValue.originalSize, `${pathArg}.snapshot.originalSize`, 1, corestoreDatabaseBackupRuntimeLimits.maximumSnapshotOriginalBytes, ); const storedSize = readCorestoreDatabaseBackupInteger( snapshotValue.storedSize, `${pathArg}.snapshot.storedSize`, 1, corestoreDatabaseBackupRuntimeLimits.maximumClosureBytes, ); const databaseAllocation = hasDatabaseAllocation ? normalizeCorestoreDatabaseAllocationReferenceInternal( snapshotValue.databaseAllocation, `${pathArg}.snapshot.databaseAllocation`, ) : undefined; return { schemaVersion: 1, format: 'corestore.database.backup.v1', backupId, sourceServiceId, createdAt, snapshot: { capability: 'database', resourceName, snapshotId, snapshotName, originalSize, storedSize, createdAt: snapshotCreatedAt, tags: { corestore: 'resource', serviceId: sourceServiceId, serviceName, capability: 'database', resourceName, snapshotName, backupId, }, databaseName, ...(databaseAllocation ? { databaseAllocation } : {}), }, closure: { formatVersion: closureValue.formatVersion === 1 ? 1 : failCorestoreDatabaseBackup(`${pathArg}.closure formatVersion must be 1`), snapshotIds: [snapshotId], objectCount, packCount, chunkCount, payloadBytes: readCorestoreDatabaseBackupInteger( closureValue.payloadBytes, `${pathArg}.closure.payloadBytes`, 1, corestoreDatabaseBackupRuntimeLimits.maximumClosureBytes, ), archiveBytes: readCorestoreDatabaseBackupInteger( closureValue.archiveBytes, `${pathArg}.closure.archiveBytes`, 1, corestoreDatabaseBackupRuntimeLimits.maximumClosureBytes, ), sha256: readCorestoreDatabaseBackupSha256( closureValue.sha256, `${pathArg}.closure.sha256`, ), }, }; }; export const normalizeCorestoreDatabaseBackupReceipt = ( valueArg: unknown, ): Readonly => finalizeCorestoreDatabaseBackup( normalizeCorestoreDatabaseBackupReceiptInternal(valueArg, 'databaseBackupReceipt'), 'databaseBackupReceipt', ); export const normalizeCorestoreDatabaseBackupRestoreRequest = ( valueArg: unknown, ): Readonly => { const value = readCorestoreDatabaseBackupRecord(valueArg, 'databaseBackupRestoreRequest'); const hasAllocation = Object.hasOwn(value, 'expectedDatabaseAllocation'); const hasRestoreAttemptId = Object.hasOwn(value, 'restoreAttemptId'); assertCorestoreDatabaseBackupExactKeys(value, [ 'schemaVersion', 'targetServiceId', 'receipt', ...(hasAllocation ? ['expectedDatabaseAllocation'] : []), ...(hasRestoreAttemptId ? ['restoreAttemptId'] : []), ], 'databaseBackupRestoreRequest'); if (value.schemaVersion !== 1) { failCorestoreDatabaseBackup('databaseBackupRestoreRequest schemaVersion must be 1'); } const receipt = normalizeCorestoreDatabaseBackupReceiptInternal( value.receipt, 'databaseBackupRestoreRequest.receipt', ); const expectedDatabaseAllocation = hasAllocation ? normalizeCorestoreDatabaseAllocationReferenceInternal( value.expectedDatabaseAllocation, 'databaseBackupRestoreRequest.expectedDatabaseAllocation', ) : undefined; const restoreAttemptId = hasRestoreAttemptId ? readCorestoreDatabaseBackupIdentifier( value.restoreAttemptId, 'databaseBackupRestoreRequest.restoreAttemptId', ) : undefined; return finalizeCorestoreDatabaseBackup({ schemaVersion: 1, targetServiceId: readCorestoreDatabaseBackupIdentifier( value.targetServiceId, 'databaseBackupRestoreRequest.targetServiceId', ), receipt, ...(expectedDatabaseAllocation ? { expectedDatabaseAllocation } : {}), ...(restoreAttemptId ? { restoreAttemptId } : {}), }, 'databaseBackupRestoreRequest'); }; export const normalizeCorestoreDatabaseBackupRestoreResponse = ( valueArg: unknown, ): Readonly => { const value = readCorestoreDatabaseBackupRecord(valueArg, 'databaseBackupRestoreResponse'); assertCorestoreDatabaseBackupExactKeys(value, [ 'schemaVersion', 'ok', 'backupId', 'sourceServiceId', 'targetServiceId', 'snapshotId', 'restoreId', 'requestSha256', 'replayed', ], 'databaseBackupRestoreResponse'); if (value.schemaVersion !== 1 || value.ok !== true || typeof value.replayed !== 'boolean') { failCorestoreDatabaseBackup('databaseBackupRestoreResponse constants must be canonical'); } const replayed = value.replayed as boolean; return finalizeCorestoreDatabaseBackup({ schemaVersion: 1, ok: true, backupId: readCorestoreDatabaseBackupIdentifier( value.backupId, 'databaseBackupRestoreResponse.backupId', ), sourceServiceId: readCorestoreDatabaseBackupIdentifier( value.sourceServiceId, 'databaseBackupRestoreResponse.sourceServiceId', ), targetServiceId: readCorestoreDatabaseBackupIdentifier( value.targetServiceId, 'databaseBackupRestoreResponse.targetServiceId', ), snapshotId: readCorestoreDatabaseBackupIdentifier( value.snapshotId, 'databaseBackupRestoreResponse.snapshotId', ), restoreId: readCorestoreDatabaseBackupIdentifier( value.restoreId, 'databaseBackupRestoreResponse.restoreId', ), requestSha256: readCorestoreDatabaseBackupSha256( value.requestSha256, 'databaseBackupRestoreResponse.requestSha256', ), replayed, }, 'databaseBackupRestoreResponse'); }; const createCorestoreDatabaseBackupCanonicalJson = ( valueArg: unknown, pathArg: string, ): string => canonicalizeStrictJson(valueArg, failCorestoreDatabaseBackup, pathArg); export const encodeCorestoreDatabaseAllocationReference = ( valueArg: unknown, ): Uint8Array => new TextEncoder().encode(createCorestoreDatabaseBackupCanonicalJson( normalizeCorestoreDatabaseAllocationReference(valueArg), 'databaseAllocation', )); export const encodeCorestoreDatabaseBackupReceipt = ( valueArg: unknown, ): Uint8Array => new TextEncoder().encode(createCorestoreDatabaseBackupCanonicalJson( normalizeCorestoreDatabaseBackupReceipt(valueArg), 'databaseBackupReceipt', )); export const encodeCorestoreDatabaseBackupRestoreRequest = ( valueArg: unknown, ): Uint8Array => new TextEncoder().encode(createCorestoreDatabaseBackupCanonicalJson( normalizeCorestoreDatabaseBackupRestoreRequest(valueArg), 'databaseBackupRestoreRequest', )); export const encodeCorestoreDatabaseBackupRestoreResponse = ( valueArg: unknown, ): Uint8Array => new TextEncoder().encode(createCorestoreDatabaseBackupCanonicalJson( normalizeCorestoreDatabaseBackupRestoreResponse(valueArg), 'databaseBackupRestoreResponse', )); export const computeCorestoreDatabaseAllocationReferenceSha256 = async ( valueArg: unknown, ): Promise => createCanonicalJsonSha256Hex( createCorestoreDatabaseBackupCanonicalJson( normalizeCorestoreDatabaseAllocationReference(valueArg), 'databaseAllocation', ), failCorestoreDatabaseBackup, ); export const computeCorestoreDatabaseBackupReceiptSha256 = async ( valueArg: unknown, ): Promise => createCanonicalJsonSha256Hex( createCorestoreDatabaseBackupCanonicalJson( normalizeCorestoreDatabaseBackupReceipt(valueArg), 'databaseBackupReceipt', ), failCorestoreDatabaseBackup, ); export const computeCorestoreDatabaseBackupRestoreRequestSha256 = async ( valueArg: unknown, ): Promise => createCanonicalJsonSha256Hex( createCorestoreDatabaseBackupCanonicalJson( normalizeCorestoreDatabaseBackupRestoreRequest(valueArg), 'databaseBackupRestoreRequest', ), failCorestoreDatabaseBackup, ); export const computeCorestoreDatabaseBackupRestoreResponseSha256 = async ( valueArg: unknown, ): Promise => createCanonicalJsonSha256Hex( createCorestoreDatabaseBackupCanonicalJson( normalizeCorestoreDatabaseBackupRestoreResponse(valueArg), 'databaseBackupRestoreResponse', ), failCorestoreDatabaseBackup, );