import { canonicalizeStrictJson, createCanonicalJsonSha256Hex, deepFreezeValue, strictCanonicalJsonRules, } from '../private/canonicaljson.js'; import { coreMailCredentialVerifierPolicy, coreMailLimits, coreMailTransferProtocol, coreMailTransferTokenPolicy, coreMailWorkloadOperationPolicy, type ICoreMailBindingReconciliationStatus, type ICoreMailBindingCredentialVerifier, type ICoreMailBindingDesiredState, type ICoreMailContentDescriptor, type ICoreMailControlBootstrap, type ICoreMailDesiredState, type ICoreMailDownloadGrant, type ICoreMailEnvelope, type ICoreMailErrorData, type ICoreMailGatewayDesiredState, type ICoreMailGatewayOutboundStatus, type ICoreMailGatewayPeerDesiredState, type ICoreMailInboundDelivery, type ICoreMailInboundDeliveryPage, type ICoreMailMailbox, type ICoreMailOutboundMessageDescriptor, type ICoreMailPartStatus, type ICoreMailRecipientResolution, type ICoreMailRuntimeKeyReference, type ICoreMailReconciliationStatus, type ICoreMailReplicaIdentity, type ICoreMailSubmission, type ICoreMailTransferGrant, type ICoreMailUploadGrant, type TCoreMailCapability, type TCoreMailBindingState, type TCoreMailErrorCode, type TCoreMailSha256, type TCoreMailWorkloadOperation, } from './coremail.js'; export const coreMailCanonicalJsonRules = strictCanonicalJsonRules; export const resolveCoreMailWorkloadOperations = ( stateArg: TCoreMailBindingState, capabilitiesArg: readonly TCoreMailCapability[], ): readonly TCoreMailWorkloadOperation[] => { const operations = new Set(); for (const capability of ['outbound', 'inbound'] as const) { if (!capabilitiesArg.includes(capability)) { continue; } for (const operation of coreMailWorkloadOperationPolicy[stateArg][capability]) { operations.add(operation); } } return Object.freeze([...operations]); }; export const coreMailContractLimits = Object.freeze({ maximumIdentifierBytes: 128, maximumEnvironmentKeyBytes: 128, maximumEndpointBytes: 2_048, maximumMailboxBytes: 254, maximumBindings: 10_000, maximumCredentialsPerAuthority: 16, maximumAllowedSendersPerBinding: 1_000, maximumInboundRecipientsPerBinding: 10_000, maximumMessagesPerMinute: 100_000, maximumMessagesPerDay: 10_000_000, maximumPendingInbound: 1_000_000, maximumDisplayNameBytes: 256, maximumSubjectBytes: 768, maximumHeaderCount: 32, maximumHeaderValueBytes: 768, maximumFilenameBytes: 512, maximumContentIdBytes: 256, maximumBearerTokenBytes: 1_024, maximumRecipientResolutionMessageBytes: 512, } as const); export class CoreMailContractError extends Error { public readonly code: TCoreMailErrorCode; public constructor( reasonArg: string, codeArg: TCoreMailErrorCode = 'INVALID_REQUEST', ) { super(`CoreMail contract error: ${reasonArg}`); this.name = 'CoreMailContractError'; this.code = codeArg; } } type TStrictRecord = Record; const fail = ( reasonArg: string, codeArg: TCoreMailErrorCode = 'INVALID_REQUEST', ): never => { throw new CoreMailContractError(reasonArg, codeArg); }; const compareStrings = (leftArg: string, rightArg: string): number => leftArg < rightArg ? -1 : leftArg > rightArg ? 1 : 0; const readRecord = (valueArg: unknown, pathArg: string): TStrictRecord => { if ( !valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg) || ( Object.getPrototypeOf(valueArg) !== Object.prototype && Object.getPrototypeOf(valueArg) !== null ) ) { return fail(`${pathArg} must be a plain object`); } return valueArg as TStrictRecord; }; const assertKeys = ( recordArg: TStrictRecord, pathArg: string, requiredKeysArg: string[], optionalKeysArg: string[] = [], ): void => { const allowedKeys = new Set([...requiredKeysArg, ...optionalKeysArg]); const keys = Reflect.ownKeys(recordArg); if (keys.some((keyArg) => typeof keyArg !== 'string')) { fail(`${pathArg} must not contain symbol keys`); } for (const requiredKey of requiredKeysArg) { if (!Object.hasOwn(recordArg, requiredKey)) { fail(`${pathArg}.${requiredKey} is required`); } } for (const key of keys as string[]) { const descriptor = Object.getOwnPropertyDescriptor(recordArg, key); if ( !descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, 'value') ) { fail(`${pathArg}.${key} must be an enumerable data property`); } if (!allowedKeys.has(key)) { fail(`${pathArg}.${key} is not allowed`); } } }; const readArray = ( valueArg: unknown, pathArg: string, maximumLengthArg: number, ): unknown[] => { if (!Array.isArray(valueArg) || Object.getPrototypeOf(valueArg) !== Array.prototype) { return fail(`${pathArg} must be a plain array`); } if (valueArg.length > maximumLengthArg) { fail(`${pathArg} exceeds its maximum length of ${maximumLengthArg}`); } for (let index = 0; index < valueArg.length; index++) { if (!Object.hasOwn(valueArg, index)) { fail(`${pathArg} must not contain sparse entries`); } const descriptor = Object.getOwnPropertyDescriptor(valueArg, String(index)); if ( !descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, 'value') ) { fail(`${pathArg}[${index}] must be an enumerable data property`); } } for (const key of Reflect.ownKeys(valueArg)) { if (key === 'length') { continue; } if ( typeof key !== 'string' || !/^(?:0|[1-9][0-9]*)$/.test(key) || Number(key) >= valueArg.length ) { fail(`${pathArg} must not contain extra properties`); } } return valueArg; }; const requireString = ( valueArg: unknown, pathArg: string, maximumBytesArg: number, ): string => { if (typeof valueArg !== 'string' || valueArg.length === 0) { return fail(`${pathArg} must be a non-empty string`); } if (new TextEncoder().encode(valueArg).byteLength > maximumBytesArg) { fail(`${pathArg} exceeds ${maximumBytesArg} UTF-8 bytes`); } return valueArg; }; const identifierRegex = /^[A-Za-z0-9](?:[A-Za-z0-9._:-]{0,126}[A-Za-z0-9])?$/; const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; const contentTypeRegex = /^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+(?:; ?[a-z0-9!#$&^_.+-]+=(?:"[^"\r\n]{1,100}"|[a-z0-9!#$&^_.+-]+))*$/i; const requireIdentifier = (valueArg: unknown, pathArg: string): string => { const value = requireString( valueArg, pathArg, coreMailContractLimits.maximumIdentifierBytes, ); if (!identifierRegex.test(value)) { fail(`${pathArg} must be a canonical identifier`); } return value; }; const requireUuid = (valueArg: unknown, pathArg: string): string => { if (typeof valueArg !== 'string' || !uuidRegex.test(valueArg)) { return fail(`${pathArg} must be a canonical UUID`); } return valueArg; }; const requireContentType = (valueArg: unknown, pathArg: string): string => { if (typeof valueArg !== 'string' || !contentTypeRegex.test(valueArg)) { return fail(`${pathArg} must be a canonical content type`); } return valueArg; }; const requireDisplayText = ( valueArg: unknown, pathArg: string, maximumBytesArg: number, allowEmptyArg = false, ): string => { if ( typeof valueArg !== 'string' || (!allowEmptyArg && valueArg.length === 0) || /[\u0000-\u001f\u007f]/.test(valueArg) || new TextEncoder().encode(valueArg).byteLength > maximumBytesArg ) { return fail(`${pathArg} must be bounded display text`); } return valueArg; }; const requireSafeInteger = ( valueArg: unknown, pathArg: string, minimumArg: number, maximumArg = Number.MAX_SAFE_INTEGER, ): number => { if ( !Number.isSafeInteger(valueArg) || (valueArg as number) < minimumArg || (valueArg as number) > maximumArg ) { return fail( `${pathArg} must be a safe integer between ${minimumArg} and ${maximumArg}`, ); } return valueArg as number; }; const decodeCanonicalBase64 = ( valueArg: string, expectedBytesArg: number, pathArg: string, ): void => { if (!/^[A-Za-z0-9+/]+$/.test(valueArg)) { fail(`${pathArg} must use unpadded standard base64`); } const paddedValue = `${valueArg}${'='.repeat((4 - (valueArg.length % 4)) % 4)}`; let decoded: string; try { decoded = globalThis.atob(paddedValue); } catch { return fail(`${pathArg} must be valid base64`); } if (decoded.length !== expectedBytesArg) { fail(`${pathArg} must encode exactly ${expectedBytesArg} bytes`); } const canonical = globalThis.btoa(decoded).replace(/=+$/u, ''); if (canonical !== valueArg) { fail(`${pathArg} must be canonical unpadded base64`); } }; export const normalizeCoreMailSha256 = ( valueArg: unknown, pathArg = 'sha256', ): TCoreMailSha256 => { if ( typeof valueArg !== 'string' || !/^sha256:[0-9a-f]{64}$/.test(valueArg) ) { return fail(`${pathArg} must be a canonical sha256:<64 lowercase hex> digest`); } return valueArg as TCoreMailSha256; }; export const normalizeCoreMailCredentialVerifier = ( valueArg: unknown, pathArg = 'credential', ): ICoreMailBindingCredentialVerifier => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, ['credentialId', 'version', 'state', 'format', 'verificationHash'], ['acceptUntil'], ); const credentialId = requireIdentifier(record.credentialId, `${pathArg}.credentialId`); const version = requireSafeInteger(record.version, `${pathArg}.version`, 1); const state = record.state === 'current' || record.state === 'retiring' ? record.state : fail(`${pathArg}.state must be current or retiring`); const format = record.format === coreMailCredentialVerifierPolicy.format ? record.format : fail(`${pathArg}.format must be ${coreMailCredentialVerifierPolicy.format}`); const verificationHash = requireString( record.verificationHash, `${pathArg}.verificationHash`, 512, ); const match = verificationHash.match( /^\$argon2id\$v=19\$m=65536,t=3,p=1\$([A-Za-z0-9+/]+)\$([A-Za-z0-9+/]+)$/, ); if (!match) { return fail(`${pathArg}.verificationHash must use the canonical argon2id-v1 PHC form`); } const [, saltBase64, digestBase64] = match; decodeCanonicalBase64( saltBase64, coreMailCredentialVerifierPolicy.saltLengthBytes, `${pathArg}.verificationHash salt`, ); decodeCanonicalBase64( digestBase64, coreMailCredentialVerifierPolicy.hashLengthBytes, `${pathArg}.verificationHash digest`, ); if (state === 'current' && Object.hasOwn(record, 'acceptUntil')) { fail(`${pathArg}.acceptUntil is forbidden for a current credential`); } if (state === 'retiring' && !Object.hasOwn(record, 'acceptUntil')) { fail(`${pathArg}.acceptUntil is required for a retiring credential`); } const normalized: ICoreMailBindingCredentialVerifier = { credentialId, version, state, format, verificationHash, }; if (state === 'retiring') { normalized.acceptUntil = requireSafeInteger( record.acceptUntil, `${pathArg}.acceptUntil`, 1, ); } return deepFreezeValue(normalized); }; const normalizeCredentialSet = ( valueArg: unknown, pathArg: string, ): ICoreMailBindingCredentialVerifier[] => { const credentials = readArray( valueArg, pathArg, coreMailContractLimits.maximumCredentialsPerAuthority, ).map((entryArg, indexArg) => normalizeCoreMailCredentialVerifier(entryArg, `${pathArg}[${indexArg}]`), ); if (credentials.length === 0) { fail(`${pathArg} must contain at least one credential`); } if (credentials.filter((entryArg) => entryArg.state === 'current').length !== 1) { fail(`${pathArg} must contain exactly one current credential`); } const identities = new Set(); const versions = new Set(); for (const credential of credentials) { const identity = `${credential.credentialId}:${credential.version}`; if (identities.has(identity) || versions.has(credential.version)) { fail(`${pathArg} contains a duplicate credential identity or version`); } identities.add(identity); versions.add(credential.version); } const current = credentials.find((entryArg) => entryArg.state === 'current')!; if (credentials.some((entryArg) => entryArg.state === 'retiring' && entryArg.version >= current.version )) { fail(`${pathArg} current credential must have the greatest version`); } return credentials.sort((leftArg, rightArg) => leftArg.version - rightArg.version || compareStrings(leftArg.credentialId, rightArg.credentialId), ); }; const requireEnvironmentKey = (valueArg: unknown, pathArg: string): string => { const value = requireString( valueArg, pathArg, coreMailContractLimits.maximumEnvironmentKeyBytes, ); if (!/^[A-Z][A-Z0-9_]{0,127}$/.test(value)) { fail(`${pathArg} must be a canonical environment key`); } return value; }; export const normalizeCoreMailRuntimeKeyReference = ( valueArg: unknown, pathArg = 'runtimeKey', ): ICoreMailRuntimeKeyReference => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, ['keyId', 'version', 'state', 'secretKey'], ['acceptUntil'], ); const state = record.state === 'current' || record.state === 'retiring' ? record.state : fail(`${pathArg}.state must be current or retiring`); if (state === 'current' && Object.hasOwn(record, 'acceptUntil')) { fail(`${pathArg}.acceptUntil is forbidden for a current key`); } if (state === 'retiring' && !Object.hasOwn(record, 'acceptUntil')) { fail(`${pathArg}.acceptUntil is required for a retiring key`); } const normalized: ICoreMailRuntimeKeyReference = { keyId: requireIdentifier(record.keyId, `${pathArg}.keyId`), version: requireSafeInteger(record.version, `${pathArg}.version`, 1), state, secretKey: requireEnvironmentKey(record.secretKey, `${pathArg}.secretKey`), }; if (state === 'retiring') { normalized.acceptUntil = requireSafeInteger( record.acceptUntil, `${pathArg}.acceptUntil`, 1, ); } return deepFreezeValue(normalized); }; const normalizeRuntimeKeySet = ( valueArg: unknown, pathArg: string, ): ICoreMailRuntimeKeyReference[] => { const keys = readArray( valueArg, pathArg, coreMailContractLimits.maximumCredentialsPerAuthority, ).map((entryArg, indexArg) => normalizeCoreMailRuntimeKeyReference(entryArg, `${pathArg}[${indexArg}]`), ); if (keys.length === 0 || keys.filter((entryArg) => entryArg.state === 'current').length !== 1) { fail(`${pathArg} must contain exactly one current key`); } const keyIds = new Set(); const versions = new Set(); const secretKeys = new Set(); for (const key of keys) { if ( keyIds.has(key.keyId) || versions.has(key.version) || secretKeys.has(key.secretKey) ) { fail(`${pathArg} contains a duplicate keyId, version, or secretKey`); } keyIds.add(key.keyId); versions.add(key.version); secretKeys.add(key.secretKey); } const current = keys.find((entryArg) => entryArg.state === 'current')!; if (keys.some((entryArg) => entryArg.state === 'retiring' && entryArg.version >= current.version )) { fail(`${pathArg} current key must have the greatest version`); } return keys.sort((leftArg, rightArg) => leftArg.version - rightArg.version || compareStrings(leftArg.keyId, rightArg.keyId), ); }; const mailboxLocalPartRegex = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+$/; const mailboxDomainLabelRegex = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/; const requireCanonicalMailbox = (valueArg: unknown, pathArg: string): string => { const value = requireString( valueArg, pathArg, coreMailContractLimits.maximumMailboxBytes, ); const atIndex = value.lastIndexOf('@'); const localPart = atIndex > 0 ? value.slice(0, atIndex) : ''; const domain = atIndex > 0 ? value.slice(atIndex + 1) : ''; const domainLabels = domain.split('.'); if ( value !== value.toLowerCase() || localPart.length === 0 || localPart.length > 64 || !mailboxLocalPartRegex.test(localPart) || localPart.startsWith('.') || localPart.endsWith('.') || localPart.includes('..') || domain.length === 0 || domain.length > 253 || domainLabels.length < 2 || domainLabels.some((labelArg) => !mailboxDomainLabelRegex.test(labelArg)) ) { fail(`${pathArg} must be a canonical lowercase mailbox`); } return value; }; const normalizeMailbox = (valueArg: unknown, pathArg: string): ICoreMailMailbox => { const record = readRecord(valueArg, pathArg); assertKeys(record, pathArg, ['address'], ['displayName']); const normalized: ICoreMailMailbox = { address: requireCanonicalMailbox(record.address, `${pathArg}.address`), }; if (Object.hasOwn(record, 'displayName')) { normalized.displayName = requireDisplayText( record.displayName, `${pathArg}.displayName`, coreMailContractLimits.maximumDisplayNameBytes, ); } return normalized; }; const normalizeMailboxArray = ( valueArg: unknown, pathArg: string, allowEmptyArg: boolean, ): ICoreMailMailbox[] => { const values = readArray(valueArg, pathArg, coreMailLimits.recipientCount); if (!allowEmptyArg && values.length === 0) { fail(`${pathArg} must not be empty`); } const normalized = values.map((entryArg, indexArg) => normalizeMailbox(entryArg, `${pathArg}[${indexArg}]`), ); if (new Set(normalized.map((entryArg) => entryArg.address)).size !== normalized.length) { fail(`${pathArg} must contain unique mailbox addresses`); } return normalized; }; export const normalizeCoreMailEnvelope = ( valueArg: unknown, pathArg = 'envelope', ): ICoreMailEnvelope => { const record = readRecord(valueArg, pathArg); assertKeys(record, pathArg, ['mailFrom', 'rcptTo']); const mailFrom = record.mailFrom === '' ? '' : requireCanonicalMailbox(record.mailFrom, `${pathArg}.mailFrom`); const rcptTo = readArray( record.rcptTo, `${pathArg}.rcptTo`, coreMailLimits.recipientCount, ).map((entryArg, indexArg) => requireCanonicalMailbox(entryArg, `${pathArg}.rcptTo[${indexArg}]`), ); if (rcptTo.length === 0 || new Set(rcptTo).size !== rcptTo.length) { fail(`${pathArg}.rcptTo must contain unique recipients`); } return deepFreezeValue({ mailFrom, rcptTo }); }; const requireCoreMailOpaqueToken = (valueArg: unknown, pathArg: string): string => { const value = requireString( valueArg, pathArg, coreMailContractLimits.maximumBearerTokenBytes, ); let decodedValue: string; try { decodedValue = globalThis.atob( `${value.replace(/-/g, '+').replace(/_/g, '/')}${'='.repeat((4 - (value.length % 4)) % 4)}`, ); } catch { return fail(`${pathArg} must use ${coreMailTransferTokenPolicy.format}`); } const canonicalValue = globalThis.btoa(decodedValue) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/u, ''); if ( decodedValue.length !== coreMailTransferTokenPolicy.decodedBytes || value.length !== coreMailTransferTokenPolicy.encodedCharacters || canonicalValue !== value ) { fail(`${pathArg} must use ${coreMailTransferTokenPolicy.format}`); } return value; }; export const normalizeCoreMailRecipientResolution = ( valueArg: unknown, pathArg = 'recipientResolution', ): ICoreMailRecipientResolution => { const record = readRecord(valueArg, pathArg); const actionDescriptor = Object.getOwnPropertyDescriptor(record, 'action'); if ( !actionDescriptor || !actionDescriptor.enumerable || !Object.hasOwn(actionDescriptor, 'value') ) { return fail(`${pathArg}.action must be an enumerable data property`); } const action = actionDescriptor.value; if (action === 'accept') { assertKeys(record, pathArg, ['recipient', 'action', 'routingHandle']); return deepFreezeValue({ recipient: requireCanonicalMailbox(record.recipient, `${pathArg}.recipient`), action, routingHandle: requireCoreMailOpaqueToken( record.routingHandle, `${pathArg}.routingHandle`, ), }); } if (action === 'reject' || action === 'defer') { assertKeys(record, pathArg, ['recipient', 'action', 'smtpCode', 'message']); const minimumCode = action === 'reject' ? 500 : 400; const maximumCode = action === 'reject' ? 599 : 499; return deepFreezeValue({ recipient: requireCanonicalMailbox(record.recipient, `${pathArg}.recipient`), action, smtpCode: requireSafeInteger( record.smtpCode, `${pathArg}.smtpCode`, minimumCode, maximumCode, ), message: requireDisplayText( record.message, `${pathArg}.message`, coreMailContractLimits.maximumRecipientResolutionMessageBytes, ), }); } if (action === 'unhandled') { assertKeys(record, pathArg, ['recipient', 'action']); return deepFreezeValue({ recipient: requireCanonicalMailbox(record.recipient, `${pathArg}.recipient`), action, }); } return fail(`${pathArg}.action must be accept, reject, defer, or unhandled`); }; export const normalizeCoreMailRecipientResolutions = ( valueArg: unknown, expectedRecipientsArg: readonly string[], pathArg = 'recipientResolutions', ): readonly ICoreMailRecipientResolution[] => { const expectedRecipients = readArray( expectedRecipientsArg, `${pathArg}.expectedRecipients`, coreMailLimits.recipientCount, ).map((recipientArg, indexArg) => requireCanonicalMailbox( recipientArg, `${pathArg}.expectedRecipients[${indexArg}]`, ), ); if ( expectedRecipients.length === 0 || new Set(expectedRecipients).size !== expectedRecipients.length ) { fail(`${pathArg}.expectedRecipients must contain unique recipients`); } const resolutions = readArray( valueArg, pathArg, coreMailLimits.recipientCount, ).map((resolutionArg, indexArg) => normalizeCoreMailRecipientResolution(resolutionArg, `${pathArg}[${indexArg}]`), ); const byRecipient = new Map(); for (const resolution of resolutions) { if (byRecipient.has(resolution.recipient)) { fail(`${pathArg} contains a duplicate recipient resolution`); } byRecipient.set(resolution.recipient, resolution); } if ( resolutions.length !== expectedRecipients.length || resolutions.some((resolutionArg) => !expectedRecipients.includes(resolutionArg.recipient)) ) { fail(`${pathArg} must resolve the exact expected recipient set`); } return deepFreezeValue(expectedRecipients.map((recipientArg) => { const resolution = byRecipient.get(recipientArg); if (!resolution) { return fail(`${pathArg} is missing a recipient resolution`); } return { ...resolution }; })); }; const forbiddenHeaderNames = new Set([ 'bcc', 'cc', 'content-transfer-encoding', 'content-type', 'date', 'delivered-to', 'dkim-signature', 'domainkey-signature', 'from', 'message-id', 'mime-version', 'received', 'received-spf', 'reply-to', 'return-path', 'sender', 'subject', 'to', 'x-original-to', ]); const isTransportOwnedHeader = (nameArg: string): boolean => forbiddenHeaderNames.has(nameArg) || nameArg === 'authentication-results' || nameArg.startsWith('arc-') || nameArg.startsWith('resent-') || nameArg.startsWith('x-coremail-'); const normalizeContentDescriptor = ( valueArg: unknown, pathArg: string, ): ICoreMailContentDescriptor => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, ['partId', 'kind', 'contentType', 'sha256', 'lengthBytes'], ['filename', 'contentId'], ); const kind = record.kind === 'text' || record.kind === 'html' || record.kind === 'attachment' ? record.kind : fail(`${pathArg}.kind is invalid`); const maximumBytes = kind === 'text' ? coreMailLimits.textPartBytes : kind === 'html' ? coreMailLimits.htmlPartBytes : coreMailLimits.attachmentBytes; const normalized: ICoreMailContentDescriptor = { partId: requireIdentifier(record.partId, `${pathArg}.partId`), kind, contentType: requireContentType(record.contentType, `${pathArg}.contentType`), sha256: normalizeCoreMailSha256(record.sha256, `${pathArg}.sha256`), lengthBytes: requireSafeInteger( record.lengthBytes, `${pathArg}.lengthBytes`, 0, ), }; if (normalized.lengthBytes > maximumBytes) { fail( `${pathArg}.lengthBytes exceeds the ${kind} content budget of ${maximumBytes}`, 'PAYLOAD_LIMIT_EXCEEDED', ); } if (Object.hasOwn(record, 'filename')) { normalized.filename = requireDisplayText( record.filename, `${pathArg}.filename`, coreMailContractLimits.maximumFilenameBytes, ); } if (Object.hasOwn(record, 'contentId')) { normalized.contentId = requireDisplayText( record.contentId, `${pathArg}.contentId`, coreMailContractLimits.maximumContentIdBytes, ); } return normalized; }; export const normalizeCoreMailOutboundMessageDescriptor = ( valueArg: unknown, pathArg = 'message', ): ICoreMailOutboundMessageDescriptor => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, ['sender', 'recipients', 'subject', 'parts'], ['replyTo', 'headers'], ); const recipientsRecord = readRecord(record.recipients, `${pathArg}.recipients`); assertKeys(recipientsRecord, `${pathArg}.recipients`, ['to'], ['cc', 'bcc']); const to = normalizeMailboxArray(recipientsRecord.to, `${pathArg}.recipients.to`, false); const cc = Object.hasOwn(recipientsRecord, 'cc') ? normalizeMailboxArray(recipientsRecord.cc, `${pathArg}.recipients.cc`, true) : undefined; const bcc = Object.hasOwn(recipientsRecord, 'bcc') ? normalizeMailboxArray(recipientsRecord.bcc, `${pathArg}.recipients.bcc`, true) : undefined; const recipientAddresses = [...to, ...(cc || []), ...(bcc || [])] .map((entryArg) => entryArg.address); if ( recipientAddresses.length > coreMailLimits.recipientCount || new Set(recipientAddresses).size !== recipientAddresses.length ) { fail(`${pathArg}.recipients must contain at most ${coreMailLimits.recipientCount} unique addresses`); } const parts = readArray( record.parts, `${pathArg}.parts`, coreMailLimits.attachmentCount + 2, ).map((entryArg, indexArg) => normalizeContentDescriptor(entryArg, `${pathArg}.parts[${indexArg}]`), ); if ( parts.length === 0 || new Set(parts.map((entryArg) => entryArg.partId)).size !== parts.length || parts.filter((entryArg) => entryArg.kind === 'text').length > 1 || parts.filter((entryArg) => entryArg.kind === 'html').length > 1 || parts.filter((entryArg) => entryArg.kind === 'attachment').length > coreMailLimits.attachmentCount || parts.filter((entryArg) => entryArg.kind === 'attachment') .reduce((totalArg, entryArg) => totalArg + entryArg.lengthBytes, 0) > coreMailLimits.aggregateAttachmentBytes ) { fail(`${pathArg}.parts is invalid`); } let headers: ICoreMailOutboundMessageDescriptor['headers']; if (Object.hasOwn(record, 'headers')) { const seenHeaderNames = new Set(); headers = readArray( record.headers, `${pathArg}.headers`, coreMailContractLimits.maximumHeaderCount, ).map((entryArg, indexArg) => { const headerPath = `${pathArg}.headers[${indexArg}]`; const headerRecord = readRecord(entryArg, headerPath); assertKeys(headerRecord, headerPath, ['name', 'value']); if ( typeof headerRecord.name !== 'string' || !/^[A-Za-z][A-Za-z0-9-]{0,62}$/.test(headerRecord.name) ) { return fail(`${headerPath}.name is invalid`); } const lowerName = headerRecord.name.toLowerCase(); if (isTransportOwnedHeader(lowerName) || seenHeaderNames.has(lowerName)) { return fail(`${headerPath}.name is transport-owned or duplicated`); } seenHeaderNames.add(lowerName); return { name: headerRecord.name, value: requireDisplayText( headerRecord.value, `${headerPath}.value`, coreMailContractLimits.maximumHeaderValueBytes, true, ), }; }); } const normalized: ICoreMailOutboundMessageDescriptor = { sender: normalizeMailbox(record.sender, `${pathArg}.sender`), recipients: { to, ...(cc ? { cc } : {}), ...(bcc ? { bcc } : {}), }, subject: requireDisplayText( record.subject, `${pathArg}.subject`, coreMailContractLimits.maximumSubjectBytes, true, ), parts, }; if (Object.hasOwn(record, 'replyTo')) { normalized.replyTo = normalizeMailbox(record.replyTo, `${pathArg}.replyTo`); } if (headers) { normalized.headers = headers; } return deepFreezeValue(normalized); }; export const normalizeCoreMailInboundPageLimit = (valueArg: unknown): number => requireSafeInteger(valueArg, 'inboundPageLimit', 1, coreMailLimits.inboundPageSize); const coreMailErrorCodes = new Set([ 'AUTHENTICATION_FAILED', 'AUTHENTICATION_EXPIRED', 'AUTHORITY_REVOKED', 'CAPABILITY_DENIED', 'NOT_FOUND', 'INVALID_REQUEST', 'INVALID_MAILBOX', 'INVALID_SENDER', 'INVALID_RECIPIENT', 'INVALID_HEADER', 'PAYLOAD_LIMIT_EXCEEDED', 'QUOTA_EXCEEDED', 'IDEMPOTENCY_CONFLICT', 'STATE_CONFLICT', 'TRANSFER_GRANT_EXPIRED', 'TRANSFER_GRANT_REPLAYED', 'TRANSFER_LENGTH_MISMATCH', 'TRANSFER_DIGEST_MISMATCH', 'TRANSFER_ENCODING_UNSUPPORTED', 'OBJECT_INTEGRITY_CONFLICT', 'DELIVERY_NOT_FETCHED', 'DELIVERY_DEFERRED', 'DELIVERY_FAILED', 'DELIVERY_DEAD_LETTERED', 'GATEWAY_UNAVAILABLE', 'RECONCILIATION_FENCE_MISMATCH', ]); export const normalizeCoreMailErrorData = ( valueArg: unknown, pathArg = 'error', ): ICoreMailErrorData => { const record = readRecord(valueArg, pathArg); assertKeys(record, pathArg, ['code', 'retryable'], ['retryAfterMs']); if (typeof record.code !== 'string' || !coreMailErrorCodes.has(record.code as TCoreMailErrorCode)) { fail(`${pathArg}.code is invalid`); } if (typeof record.retryable !== 'boolean') { fail(`${pathArg}.retryable must be boolean`); } const normalized: ICoreMailErrorData = { code: record.code as TCoreMailErrorCode, retryable: record.retryable as boolean, }; if (Object.hasOwn(record, 'retryAfterMs')) { if (!record.retryable) { fail(`${pathArg}.retryAfterMs requires retryable to be true`); } normalized.retryAfterMs = requireSafeInteger( record.retryAfterMs, `${pathArg}.retryAfterMs`, 1, ); } return deepFreezeValue(normalized); }; export const normalizeCoreMailTransferGrant = ( valueArg: unknown, pathArg = 'transferGrant', ): ICoreMailTransferGrant => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, [ 'grantId', 'method', 'path', 'bearerToken', 'sha256', 'lengthBytes', 'contentType', 'issuedAt', 'expiresAt', ], ); const grantId = requireUuid(record.grantId, `${pathArg}.grantId`); const method = record.method === 'PUT' || record.method === 'GET' ? record.method : fail(`${pathArg}.method must be PUT or GET`); const path = requireString(record.path, `${pathArg}.path`, 128); if (path !== `${coreMailTransferProtocol.pathPrefix}${grantId}`) { fail(`${pathArg}.path must match its grantId`); } const bearerToken = requireCoreMailOpaqueToken( record.bearerToken, `${pathArg}.bearerToken`, ); const issuedAt = requireSafeInteger(record.issuedAt, `${pathArg}.issuedAt`, 1); const expiresAt = requireSafeInteger(record.expiresAt, `${pathArg}.expiresAt`, 1); if (expiresAt - issuedAt !== coreMailLimits.transferGrantTtlMs) { fail(`${pathArg} must use the fixed transfer grant lifetime`); } return deepFreezeValue({ grantId, method, path, bearerToken, sha256: normalizeCoreMailSha256(record.sha256, `${pathArg}.sha256`), lengthBytes: requireSafeInteger( record.lengthBytes, `${pathArg}.lengthBytes`, 0, coreMailLimits.serializedMimeBytes, ), contentType: requireContentType(record.contentType, `${pathArg}.contentType`), issuedAt, expiresAt, }); }; export const normalizeCoreMailUploadGrant = ( valueArg: unknown, pathArg = 'uploadGrant', ): ICoreMailUploadGrant => { const grant = normalizeCoreMailTransferGrant(valueArg, pathArg); if (grant.method !== 'PUT') { fail(`${pathArg}.method must be PUT`); } return grant as ICoreMailUploadGrant; }; export const normalizeCoreMailDownloadGrant = ( valueArg: unknown, pathArg = 'downloadGrant', ): ICoreMailDownloadGrant => { const grant = normalizeCoreMailTransferGrant(valueArg, pathArg); if (grant.method !== 'GET') { fail(`${pathArg}.method must be GET`); } return grant as ICoreMailDownloadGrant; }; const normalizePartStatus = (valueArg: unknown, pathArg: string): ICoreMailPartStatus => { const record = readRecord(valueArg, pathArg); assertKeys(record, pathArg, ['partId', 'state', 'sha256', 'lengthBytes']); const state = record.state === 'missing' || record.state === 'uploading' || record.state === 'complete' || record.state === 'failed' ? record.state : fail(`${pathArg}.state is invalid`); return { partId: requireIdentifier(record.partId, `${pathArg}.partId`), state, sha256: normalizeCoreMailSha256(record.sha256, `${pathArg}.sha256`), lengthBytes: requireSafeInteger( record.lengthBytes, `${pathArg}.lengthBytes`, 0, coreMailLimits.attachmentBytes, ), }; }; const assertOutboundStateFields = ( stateArg: ICoreMailSubmission['state'], valueArg: { nextAttemptAt?: number; error?: ICoreMailErrorData; deliveredAt?: number; terminalAt?: number; }, pathArg: string, ): void => { if (stateArg === 'deferred') { if ( valueArg.nextAttemptAt === undefined || !valueArg.error?.retryable || valueArg.terminalAt !== undefined || valueArg.deliveredAt !== undefined ) { fail(`${pathArg} deferred state fields are inconsistent`); } return; } if (stateArg === 'failed' || stateArg === 'deadLettered') { if ( valueArg.terminalAt === undefined || !valueArg.error || valueArg.error.retryable || valueArg.nextAttemptAt !== undefined || valueArg.deliveredAt !== undefined ) { fail(`${pathArg} terminal failure fields are inconsistent`); } return; } if (stateArg === 'delivered') { if ( valueArg.terminalAt === undefined || valueArg.deliveredAt !== valueArg.terminalAt || valueArg.error !== undefined || valueArg.nextAttemptAt !== undefined ) { fail(`${pathArg} delivered state fields are inconsistent`); } return; } if ( valueArg.error !== undefined || valueArg.terminalAt !== undefined || valueArg.deliveredAt !== undefined || valueArg.nextAttemptAt !== undefined ) { fail(`${pathArg} state contains fields reserved for deferred or terminal outcomes`); } }; const submissionStates = new Set([ 'preparing', 'uploading', 'ready', 'accepted', 'queued', 'delivering', 'delivered', 'deferred', 'failed', 'deadLettered', ]); export const normalizeCoreMailSubmission = ( valueArg: unknown, pathArg = 'submission', ): ICoreMailSubmission => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, [ 'submissionId', 'idempotencyKey', 'submissionDigest', 'state', 'parts', 'attempts', 'createdAt', 'updatedAt', ], [ 'transportMessageId', 'nextAttemptAt', 'error', 'acceptedAt', 'deliveredAt', 'terminalAt', ], ); if (typeof record.state !== 'string' || !submissionStates.has(record.state as ICoreMailSubmission['state'])) { fail(`${pathArg}.state is invalid`); } const parts = readArray( record.parts, `${pathArg}.parts`, coreMailLimits.attachmentCount + 2, ).map((entryArg, indexArg) => normalizePartStatus(entryArg, `${pathArg}.parts[${indexArg}]`), ); if ( parts.length === 0 || new Set(parts.map((entryArg) => entryArg.partId)).size !== parts.length ) { fail(`${pathArg}.parts must contain unique part identities`); } const normalized: ICoreMailSubmission = { submissionId: requireIdentifier(record.submissionId, `${pathArg}.submissionId`), idempotencyKey: requireString(record.idempotencyKey, `${pathArg}.idempotencyKey`, 256), submissionDigest: normalizeCoreMailSha256( record.submissionDigest, `${pathArg}.submissionDigest`, ), state: record.state as ICoreMailSubmission['state'], parts, attempts: requireSafeInteger(record.attempts, `${pathArg}.attempts`, 0), createdAt: requireSafeInteger(record.createdAt, `${pathArg}.createdAt`, 1), updatedAt: requireSafeInteger(record.updatedAt, `${pathArg}.updatedAt`, 1), }; for (const key of [ 'nextAttemptAt', 'acceptedAt', 'deliveredAt', 'terminalAt', ] as const) { if (Object.hasOwn(record, key)) { normalized[key] = requireSafeInteger(record[key], `${pathArg}.${key}`, 1); } } if (Object.hasOwn(record, 'transportMessageId')) { normalized.transportMessageId = requireIdentifier( record.transportMessageId, `${pathArg}.transportMessageId`, ); } if (Object.hasOwn(record, 'error')) { normalized.error = normalizeCoreMailErrorData(record.error, `${pathArg}.error`); } assertOutboundStateFields(normalized.state, normalized, pathArg); const hasTransportIdentity = normalized.transportMessageId !== undefined; const hasAcceptedAt = normalized.acceptedAt !== undefined; if (hasTransportIdentity !== hasAcceptedAt) { fail(`${pathArg}.transportMessageId and acceptedAt must appear together`); } if ( normalized.state === 'accepted' || normalized.state === 'queued' || normalized.state === 'delivering' || normalized.state === 'delivered' || normalized.state === 'deferred' || normalized.state === 'deadLettered' ) { if (!hasTransportIdentity) { fail(`${pathArg} state requires transport acceptance fields`); } } else if ( (normalized.state === 'preparing' || normalized.state === 'uploading' || normalized.state === 'ready') && hasTransportIdentity ) { fail(`${pathArg} state must not contain transport acceptance fields`); } if ( normalized.updatedAt < normalized.createdAt || ( normalized.acceptedAt !== undefined && ( normalized.acceptedAt < normalized.createdAt || normalized.acceptedAt > normalized.updatedAt ) ) || ( normalized.terminalAt !== undefined && ( normalized.terminalAt < (normalized.acceptedAt || normalized.createdAt) || normalized.terminalAt > normalized.updatedAt ) ) || ( normalized.nextAttemptAt !== undefined && normalized.nextAttemptAt < normalized.updatedAt ) ) { fail(`${pathArg} timestamps are not chronologically ordered`); } if ( (normalized.state === 'ready' || normalized.state === 'accepted' || normalized.state === 'queued' || normalized.state === 'delivering' || normalized.state === 'delivered' || normalized.state === 'deferred' || normalized.state === 'deadLettered' || (normalized.state === 'failed' && hasTransportIdentity)) && normalized.parts.some((entryArg) => entryArg.state !== 'complete') ) { fail(`${pathArg} state requires every part to be complete`); } return deepFreezeValue(normalized); }; export const normalizeCoreMailGatewayOutboundStatus = ( valueArg: unknown, pathArg = 'gatewayStatus', ): ICoreMailGatewayOutboundStatus => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, ['transportMessageId', 'state', 'attempts', 'updatedAt'], ['nextAttemptAt', 'error', 'smtpCode', 'deliveredAt', 'terminalAt'], ); if ( record.state !== 'accepted' && record.state !== 'queued' && record.state !== 'delivering' && record.state !== 'delivered' && record.state !== 'deferred' && record.state !== 'failed' && record.state !== 'deadLettered' ) { fail(`${pathArg}.state is invalid`); } const normalized: ICoreMailGatewayOutboundStatus = { transportMessageId: requireIdentifier( record.transportMessageId, `${pathArg}.transportMessageId`, ), state: record.state as ICoreMailGatewayOutboundStatus['state'], attempts: requireSafeInteger(record.attempts, `${pathArg}.attempts`, 0), updatedAt: requireSafeInteger(record.updatedAt, `${pathArg}.updatedAt`, 1), }; for (const key of ['nextAttemptAt', 'deliveredAt', 'terminalAt'] as const) { if (Object.hasOwn(record, key)) { normalized[key] = requireSafeInteger(record[key], `${pathArg}.${key}`, 1); } } if (Object.hasOwn(record, 'smtpCode')) { normalized.smtpCode = requireSafeInteger(record.smtpCode, `${pathArg}.smtpCode`, 100, 599); } if (Object.hasOwn(record, 'error')) { normalized.error = normalizeCoreMailErrorData(record.error, `${pathArg}.error`); } assertOutboundStateFields(normalized.state, normalized, pathArg); if ( (normalized.deliveredAt !== undefined && normalized.deliveredAt > normalized.updatedAt) || (normalized.terminalAt !== undefined && normalized.terminalAt > normalized.updatedAt) || (normalized.nextAttemptAt !== undefined && normalized.nextAttemptAt < normalized.updatedAt) ) { fail(`${pathArg} timestamps are not chronologically ordered`); } return deepFreezeValue(normalized); }; export const normalizeCoreMailInboundDelivery = ( valueArg: unknown, pathArg = 'inboundDelivery', ): ICoreMailInboundDelivery => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, [ 'deliveryId', 'transportDeliveryId', 'state', 'envelope', 'rawMime', 'receivedAt', 'updatedAt', ], ['messageId', 'subject', 'acknowledgedAt', 'acknowledgedOutcome'], ); const state = record.state === 'pending' || record.state === 'fetching' || record.state === 'fetched' || record.state === 'acknowledged' ? record.state : fail(`${pathArg}.state is invalid`); const rawMime = readRecord(record.rawMime, `${pathArg}.rawMime`); assertKeys(rawMime, `${pathArg}.rawMime`, ['sha256', 'lengthBytes', 'contentType']); if (rawMime.contentType !== 'message/rfc822') { fail(`${pathArg}.rawMime.contentType must be message/rfc822`); } const normalized: ICoreMailInboundDelivery = { deliveryId: requireIdentifier(record.deliveryId, `${pathArg}.deliveryId`), transportDeliveryId: requireIdentifier( record.transportDeliveryId, `${pathArg}.transportDeliveryId`, ), state, envelope: normalizeCoreMailEnvelope(record.envelope, `${pathArg}.envelope`), rawMime: { sha256: normalizeCoreMailSha256(rawMime.sha256, `${pathArg}.rawMime.sha256`), lengthBytes: requireSafeInteger( rawMime.lengthBytes, `${pathArg}.rawMime.lengthBytes`, 1, coreMailLimits.serializedMimeBytes, ), contentType: 'message/rfc822', }, receivedAt: requireSafeInteger(record.receivedAt, `${pathArg}.receivedAt`, 1), updatedAt: requireSafeInteger(record.updatedAt, `${pathArg}.updatedAt`, 1), }; if (Object.hasOwn(record, 'messageId')) { normalized.messageId = requireDisplayText(record.messageId, `${pathArg}.messageId`, 998); } if (Object.hasOwn(record, 'subject')) { normalized.subject = requireDisplayText( record.subject, `${pathArg}.subject`, coreMailContractLimits.maximumSubjectBytes, true, ); } if (state === 'acknowledged') { if ( !Object.hasOwn(record, 'acknowledgedAt') || (record.acknowledgedOutcome !== 'processed' && record.acknowledgedOutcome !== 'discarded') ) { fail(`${pathArg} acknowledged state fields are incomplete`); } normalized.acknowledgedAt = requireSafeInteger( record.acknowledgedAt, `${pathArg}.acknowledgedAt`, 1, ); normalized.acknowledgedOutcome = record.acknowledgedOutcome as ICoreMailInboundDelivery['acknowledgedOutcome']; } else if ( Object.hasOwn(record, 'acknowledgedAt') || Object.hasOwn(record, 'acknowledgedOutcome') ) { fail(`${pathArg} nonacknowledged state contains acknowledgement fields`); } if ( normalized.updatedAt < normalized.receivedAt || ( normalized.acknowledgedAt !== undefined && ( normalized.acknowledgedAt < normalized.receivedAt || normalized.acknowledgedAt > normalized.updatedAt ) ) ) { fail(`${pathArg} timestamps are not chronologically ordered`); } return deepFreezeValue(normalized); }; export const normalizeCoreMailInboundCursor = ( valueArg: unknown, pathArg = 'inboundCursor', ): string => { const cursor = requireString(valueArg, pathArg, coreMailLimits.cursorBytes); let decodedCursor: string; try { decodedCursor = globalThis.atob( `${cursor.replace(/-/g, '+').replace(/_/g, '/')}${'='.repeat((4 - (cursor.length % 4)) % 4)}`, ); } catch { return fail(`${pathArg} must be a canonical opaque base64url value`); } const canonicalCursor = globalThis.btoa(decodedCursor) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/u, ''); if ( decodedCursor.length < coreMailLimits.cursorMinimumDecodedBytes || canonicalCursor !== cursor ) { fail(`${pathArg} must be a canonical opaque base64url value`); } return cursor; }; export const normalizeCoreMailInboundDeliveryPage = ( valueArg: unknown, pathArg = 'inboundDeliveryPage', ): ICoreMailInboundDeliveryPage => { const record = readRecord(valueArg, pathArg); assertKeys(record, pathArg, ['deliveries'], ['nextCursor']); const deliveries = readArray( record.deliveries, `${pathArg}.deliveries`, coreMailLimits.inboundPageSize, ).map((entryArg, indexArg) => normalizeCoreMailInboundDelivery(entryArg, `${pathArg}.deliveries[${indexArg}]`), ); if (new Set(deliveries.map((entryArg) => entryArg.deliveryId)).size !== deliveries.length) { fail(`${pathArg}.deliveries must contain unique delivery identities`); } const normalized: ICoreMailInboundDeliveryPage = { deliveries }; if (Object.hasOwn(record, 'nextCursor')) { normalized.nextCursor = normalizeCoreMailInboundCursor( record.nextCursor, `${pathArg}.nextCursor`, ); } if ( new TextEncoder().encode(JSON.stringify(normalized)).byteLength > coreMailLimits.inboundPageBytes ) { fail(`${pathArg} exceeds the inbound page byte budget`); } return deepFreezeValue(normalized); }; const normalizeUniqueSortedStrings = ( valueArg: unknown, pathArg: string, maximumLengthArg: number, normalizeEntryArg: (entryArg: unknown, pathArg: string) => string, ): string[] => { const values = readArray(valueArg, pathArg, maximumLengthArg).map( (entryArg, indexArg) => normalizeEntryArg(entryArg, `${pathArg}[${indexArg}]`), ); if (new Set(values).size !== values.length) { fail(`${pathArg} must contain unique values`); } return values.sort(); }; const normalizeCapabilities = ( valueArg: unknown, pathArg: string, ): TCoreMailCapability[] => { const capabilities = readArray(valueArg, pathArg, 2).map((entryArg, indexArg) => { if (entryArg !== 'outbound' && entryArg !== 'inbound') { return fail(`${pathArg}[${indexArg}] must be outbound or inbound`); } return entryArg; }); if (capabilities.length === 0 || new Set(capabilities).size !== capabilities.length) { fail(`${pathArg} must contain unique capabilities`); } return capabilities.sort(); }; const normalizeLimits = ( valueArg: unknown, pathArg: string, ): NonNullable => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, ['messagesPerMinute', 'messagesPerDay', 'maxPendingInbound'], ); const maxima = { messagesPerMinute: coreMailContractLimits.maximumMessagesPerMinute, messagesPerDay: coreMailContractLimits.maximumMessagesPerDay, maxPendingInbound: coreMailContractLimits.maximumPendingInbound, } as const; const normalized = {} as ICoreMailBindingDesiredState['limits']; for (const key of Object.keys(maxima) as Array) { normalized[key] = requireSafeInteger( record[key], `${pathArg}.${key}`, 1, maxima[key], ); } return normalized; }; const normalizeBinding = ( valueArg: unknown, pathArg: string, ): ICoreMailBindingDesiredState => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, [ 'schemaVersion', 'bindingId', 'serviceId', 'tenantId', 'revision', 'state', 'capabilities', 'credentials', 'allowedSenders', 'inboundRecipients', 'limits', ], ['defaultSender'], ); if (record.schemaVersion !== 2) { fail(`${pathArg}.schemaVersion must be 2`); } const state = record.state === 'active' || record.state === 'draining' || record.state === 'disabled' ? record.state : fail(`${pathArg}.state must be active, draining, or disabled`); const capabilities = normalizeCapabilities(record.capabilities, `${pathArg}.capabilities`); const allowedSenders = normalizeUniqueSortedStrings( record.allowedSenders, `${pathArg}.allowedSenders`, coreMailContractLimits.maximumAllowedSendersPerBinding, requireCanonicalMailbox, ); const inboundRecipients = normalizeUniqueSortedStrings( record.inboundRecipients, `${pathArg}.inboundRecipients`, coreMailContractLimits.maximumInboundRecipientsPerBinding, requireCanonicalMailbox, ); if (!capabilities.includes('outbound') && allowedSenders.length > 0) { fail(`${pathArg}.allowedSenders requires the outbound capability`); } if (!capabilities.includes('inbound') && inboundRecipients.length > 0) { fail(`${pathArg}.inboundRecipients requires the inbound capability`); } const normalized: ICoreMailBindingDesiredState = { schemaVersion: 2, bindingId: requireIdentifier(record.bindingId, `${pathArg}.bindingId`), serviceId: requireIdentifier(record.serviceId, `${pathArg}.serviceId`), tenantId: requireIdentifier(record.tenantId, `${pathArg}.tenantId`), revision: requireSafeInteger(record.revision, `${pathArg}.revision`, 1), state, capabilities, credentials: normalizeCredentialSet(record.credentials, `${pathArg}.credentials`), allowedSenders, inboundRecipients, limits: normalizeLimits(record.limits, `${pathArg}.limits`), }; if (Object.hasOwn(record, 'defaultSender')) { normalized.defaultSender = requireCanonicalMailbox( record.defaultSender, `${pathArg}.defaultSender`, ); if (!allowedSenders.includes(normalized.defaultSender)) { fail(`${pathArg}.defaultSender must be present in allowedSenders`); } } return normalized; }; const requireCanonicalUrl = ( valueArg: unknown, pathArg: string, protocolArg: 'https:' | 'wss:', originOnlyArg: boolean, ): string => { const value = requireString( valueArg, pathArg, coreMailContractLimits.maximumEndpointBytes, ); let url: URL; try { url = new URL(value); } catch { return fail(`${pathArg} must be an absolute URL`); } if ( url.protocol !== protocolArg || url.username || url.password || url.search || url.hash ) { fail(`${pathArg} must be a credential-free canonical ${protocolArg} URL`); } if (originOnlyArg && url.pathname !== '/') { fail(`${pathArg} must be an origin without a path`); } const canonical = originOnlyArg ? url.origin : url.toString(); if (canonical !== value) { fail(`${pathArg} must already be in canonical URL form`); } return canonical; }; const normalizeGateway = ( valueArg: unknown, pathArg: string, ): ICoreMailGatewayDesiredState => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, [ 'endpointUrl', 'coreMailTransferOrigin', 'credentialId', 'credentialVersion', 'credentialSecretKey', ], ); return { endpointUrl: requireCanonicalUrl( record.endpointUrl, `${pathArg}.endpointUrl`, 'wss:', false, ), coreMailTransferOrigin: requireCanonicalUrl( record.coreMailTransferOrigin, `${pathArg}.coreMailTransferOrigin`, 'https:', true, ), credentialId: requireIdentifier(record.credentialId, `${pathArg}.credentialId`), credentialVersion: requireSafeInteger( record.credentialVersion, `${pathArg}.credentialVersion`, 1, ), credentialSecretKey: requireEnvironmentKey( record.credentialSecretKey, `${pathArg}.credentialSecretKey`, ), }; }; export const normalizeCoreMailDesiredState = ( valueArg: unknown, ): ICoreMailDesiredState => { const record = readRecord(valueArg, 'desiredState'); assertKeys( record, 'desiredState', ['schemaVersion', 'configEpoch', 'bindings', 'gateway', 'cursorKeys'], ); if (record.schemaVersion !== 2) { fail('desiredState.schemaVersion must be 2'); } const bindings = readArray( record.bindings, 'desiredState.bindings', coreMailContractLimits.maximumBindings, ).map((entryArg, indexArg) => normalizeBinding(entryArg, `desiredState.bindings[${indexArg}]`), ); const bindingIds = new Set(); const activeInboundRecipients = new Set(); for (const binding of bindings) { if (bindingIds.has(binding.bindingId)) { fail('desiredState.bindings contains a duplicate bindingId'); } bindingIds.add(binding.bindingId); if (binding.state === 'active') { for (const recipient of binding.inboundRecipients) { if (activeInboundRecipients.has(recipient)) { fail('active inbound recipient ownership must be unique'); } activeInboundRecipients.add(recipient); } } } bindings.sort((leftArg, rightArg) => compareStrings(leftArg.bindingId, rightArg.bindingId), ); return deepFreezeValue({ schemaVersion: 2, configEpoch: requireSafeInteger(record.configEpoch, 'desiredState.configEpoch', 1), bindings, gateway: normalizeGateway(record.gateway, 'desiredState.gateway'), cursorKeys: normalizeRuntimeKeySet(record.cursorKeys, 'desiredState.cursorKeys'), }); }; export const normalizeCoreMailReplicaIdentity = ( valueArg: unknown, pathArg = 'replica', ): ICoreMailReplicaIdentity => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, ['taskId', 'serviceId', 'rolloutId', 'rolloutGeneration', 'imageDigest'], ); return deepFreezeValue({ taskId: requireIdentifier(record.taskId, `${pathArg}.taskId`), serviceId: requireIdentifier(record.serviceId, `${pathArg}.serviceId`), rolloutId: requireIdentifier(record.rolloutId, `${pathArg}.rolloutId`), rolloutGeneration: requireSafeInteger( record.rolloutGeneration, `${pathArg}.rolloutGeneration`, 1, ), imageDigest: normalizeCoreMailSha256(record.imageDigest, `${pathArg}.imageDigest`), }); }; const normalizeBindingReconciliationStatus = ( valueArg: unknown, pathArg: string, ): ICoreMailBindingReconciliationStatus => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, [ 'bindingId', 'revision', 'state', 'pendingInboundCount', 'activeSessionsByCredential', ], ); const state = record.state === 'active' || record.state === 'draining' || record.state === 'disabled' ? record.state : fail(`${pathArg}.state must be active, draining, or disabled`); const activeSessionsByCredential = readArray( record.activeSessionsByCredential, `${pathArg}.activeSessionsByCredential`, coreMailContractLimits.maximumCredentialsPerAuthority, ).map((entryArg, indexArg) => { const entryPath = `${pathArg}.activeSessionsByCredential[${indexArg}]`; const entry = readRecord(entryArg, entryPath); assertKeys(entry, entryPath, ['credentialId', 'version', 'count']); return { credentialId: requireIdentifier(entry.credentialId, `${entryPath}.credentialId`), version: requireSafeInteger(entry.version, `${entryPath}.version`, 1), count: requireSafeInteger(entry.count, `${entryPath}.count`, 1), }; }); if ( new Set(activeSessionsByCredential.map((entryArg) => `${entryArg.credentialId}:${entryArg.version}` )).size !== activeSessionsByCredential.length ) { fail(`${pathArg}.activeSessionsByCredential contains duplicate identities`); } activeSessionsByCredential.sort((leftArg, rightArg) => leftArg.version - rightArg.version || compareStrings(leftArg.credentialId, rightArg.credentialId), ); return { bindingId: requireIdentifier(record.bindingId, `${pathArg}.bindingId`), revision: requireSafeInteger(record.revision, `${pathArg}.revision`, 1), state, pendingInboundCount: requireSafeInteger( record.pendingInboundCount, `${pathArg}.pendingInboundCount`, 0, coreMailContractLimits.maximumPendingInbound, ), activeSessionsByCredential, }; }; export const normalizeCoreMailReconciliationStatus = ( valueArg: unknown, pathArg = 'reconciliationStatus', ): ICoreMailReconciliationStatus => { const record = readRecord(valueArg, pathArg); assertKeys( record, pathArg, [ 'replica', 'state', 'appliedConfigEpoch', 'appliedDesiredStateDigest', 'bindings', 'updatedAt', ], ['errorCode'], ); const state = record.state === 'applying' || record.state === 'ready' || record.state === 'failed' ? record.state : fail(`${pathArg}.state must be applying, ready, or failed`); const bindings = readArray( record.bindings, `${pathArg}.bindings`, coreMailContractLimits.maximumBindings, ).map((entryArg, indexArg) => normalizeBindingReconciliationStatus(entryArg, `${pathArg}.bindings[${indexArg}]`), ); if (new Set(bindings.map((entryArg) => entryArg.bindingId)).size !== bindings.length) { fail(`${pathArg}.bindings contains duplicate binding identities`); } bindings.sort((leftArg, rightArg) => compareStrings(leftArg.bindingId, rightArg.bindingId)); const hasErrorCode = Object.hasOwn(record, 'errorCode'); if ((state === 'failed') !== hasErrorCode) { fail(`${pathArg}.errorCode must appear exactly for failed state`); } const normalized: ICoreMailReconciliationStatus = { replica: normalizeCoreMailReplicaIdentity(record.replica, `${pathArg}.replica`), state, appliedConfigEpoch: requireSafeInteger( record.appliedConfigEpoch, `${pathArg}.appliedConfigEpoch`, 1, ), appliedDesiredStateDigest: normalizeCoreMailSha256( record.appliedDesiredStateDigest, `${pathArg}.appliedDesiredStateDigest`, ), bindings, updatedAt: requireSafeInteger(record.updatedAt, `${pathArg}.updatedAt`, 1), }; if (hasErrorCode) { if ( typeof record.errorCode !== 'string' || !coreMailErrorCodes.has(record.errorCode as TCoreMailErrorCode) ) { fail(`${pathArg}.errorCode is invalid`); } normalized.errorCode = record.errorCode as TCoreMailErrorCode; } return deepFreezeValue(normalized); }; export const canonicalizeCoreMailDesiredState = ( valueArg: unknown, ): string => { return canonicalizeStrictJson( normalizeCoreMailDesiredState(valueArg), fail, 'CoreMail desired state', ); }; export const createCoreMailDesiredStateDigest = async ( valueArg: unknown, ): Promise => { const digestHex = await createCanonicalJsonSha256Hex( canonicalizeCoreMailDesiredState(valueArg), fail, ); return `sha256:${digestHex}` as TCoreMailSha256; }; export const verifyCoreMailDesiredStateDigest = async ( valueArg: unknown, digestArg: unknown, ): Promise => { return normalizeCoreMailSha256(digestArg, 'desiredStateDigest') === await createCoreMailDesiredStateDigest(valueArg); }; export const normalizeCoreMailControlBootstrap = ( valueArg: unknown, ): ICoreMailControlBootstrap => { const record = readRecord(valueArg, 'controlBootstrap'); assertKeys( record, 'controlBootstrap', ['schemaVersion', 'coreMailServiceId', 'credentials'], ); if (record.schemaVersion !== 1) { fail('controlBootstrap.schemaVersion must be 1'); } return deepFreezeValue({ schemaVersion: 1, coreMailServiceId: requireIdentifier( record.coreMailServiceId, 'controlBootstrap.coreMailServiceId', ), credentials: normalizeCredentialSet( record.credentials, 'controlBootstrap.credentials', ), }); }; export const normalizeCoreMailGatewayPeerDesiredState = ( valueArg: unknown, ): ICoreMailGatewayPeerDesiredState => { const record = readRecord(valueArg, 'gatewayPeer'); assertKeys( record, 'gatewayPeer', ['schemaVersion', 'coreMailServiceId', 'transferOrigin', 'credentials'], ); if (record.schemaVersion !== 1) { fail('gatewayPeer.schemaVersion must be 1'); } return deepFreezeValue({ schemaVersion: 1, coreMailServiceId: requireIdentifier( record.coreMailServiceId, 'gatewayPeer.coreMailServiceId', ), transferOrigin: requireCanonicalUrl( record.transferOrigin, 'gatewayPeer.transferOrigin', 'https:', true, ), credentials: normalizeCredentialSet( record.credentials, 'gatewayPeer.credentials', ), }); };