import * as plugins from '../plugins.js'; import type { TSha256Digest } from './immutableimage.js'; import { normalizeServiceAbsolutePath } from './service.js'; export type TSecretEnvironment = string; export type TSecretLifecycleState = | 'active' | 'retired' | 'revoked' | 'deleted'; export type TSecretVersionLifecycleState = | 'active' | 'retired' | 'revoked' | 'purge-pending' | 'purged'; /** Retired versions remain usable only through an already-accepted manifest. */ export const isSecretVersionMaterialIssuable = ( lifecycleStateArg: TSecretVersionLifecycleState, referencedByAcceptedManifestArg: boolean, ): boolean => lifecycleStateArg === 'active' || (lifecycleStateArg === 'retired' && referencedByAcceptedManifestArg); export type TSecretManagementSource = | 'operator' | 'appstore' | 'mail' | 'oidc' | 'webpush' | 'platform' | 'system'; export type TSecretManagementScope = | 'operator' | `appstore:${string}` | `mail:${string}` | `oidc:${string}` | `webpush:${string}` | `platform:${string}` | `system:${string}`; export interface IServiceSecretOwner { kind: 'service'; serviceId: string; } export interface ISecretSetSecretOwner { kind: 'secret-set'; secretSetId: string; } export interface IPlatformProviderSecretOwner { kind: 'platform-provider'; providerConfigId: string; } export interface ISystemSecretOwner { kind: 'system'; systemId: string; } export type TSecretOwner = | IServiceSecretOwner | ISecretSetSecretOwner | IPlatformProviderSecretOwner | ISystemSecretOwner; export type TSecretMutationTarget = TSecretOwner; export type TWorkloadSecretOwner = IServiceSecretOwner | ISecretSetSecretOwner; export interface ISecretVersionReference { secretId: string; secretVersionId: string; } export type TSecretVersionRetentionReferenceKind = | 'desired-manifest' | 'applied-manifest' | 'previous-accepted-manifest' | 'rollback-manifest' | 'backup' | 'retention-window'; export interface ISecretVersionRetentionReference { kind: TSecretVersionRetentionReferenceKind; resourceId: string; expiresAt?: number; } export const secretVersionRetentionReferenceLimits = Object.freeze({ pageSizeMaximum: 256, }); /** Browser-safe logical secret metadata. It never contains value material. */ export interface ISecretMetadata { id: string; organizationId: string; owner: TSecretOwner; key: string; environment: TSecretEnvironment; name: string; description?: string; tags: Array<{ key: string; value: string }>; managementSource: TSecretManagementSource; /** Exact reconciliation owner, for example operator or appstore:. */ managementScope: TSecretManagementScope; defaultDelivery: TSecretDelivery; lifecycleState: TSecretLifecycleState; activeVersion?: ISecretVersionReference; versionCount: number; revision: number; createdAt: number; updatedAt: number; lastRotatedAt?: number; } /** Browser-safe immutable-version metadata. It never contains an encrypted envelope. */ export interface ISecretVersionMetadata { id: string; secretId: string; revision: number; lifecycleState: TSecretVersionLifecycleState; createdAt: number; createdBy: string; source: TSecretManagementSource; retiredAt?: number; revokedAt?: number; purgeOperationId?: string; purgedAt?: number; } /** Irreversibly erases every platform-managed copy after all references are gone. */ export interface ISecretVersionPurgeIntentV1 { schemaVersion: 1; mutationId: string; secretId: string; secretVersionId: string; expectedSecretRevision: number; expectedSecretVersionRevision: number; expectedTargetSecretsRevision: number; } export interface ISecretVersionPurgeFailureV1 { code: string; retryable: boolean; failedAt: number; } interface ISecretVersionPurgeOperationBaseV1 { schemaVersion: 1; id: string; revision: number; intent: ISecretVersionPurgeIntentV1; intentDigest: TSha256Digest; createdAt: number; updatedAt: number; } export type TSecretVersionPurgeOperationV1 = | (ISecretVersionPurgeOperationBaseV1 & { state: 'pending' | 'erasing'; failure?: never; purgedAt?: never; }) | (ISecretVersionPurgeOperationBaseV1 & { state: 'failed'; failure: ISecretVersionPurgeFailureV1; purgedAt?: never; }) | (ISecretVersionPurgeOperationBaseV1 & { state: 'succeeded'; failure?: never; purgedAt: number; }); export interface ISecretSetMetadata { id: string; organizationId: string; name: string; description?: string; lifecycleState: 'active' | 'retired' | 'deleted'; revision: number; /** Dedicated CAS revision for secrets owned by this set. */ secretsRevision: number; currentConsumerRollout?: ISecretSetConsumerRolloutSummary; createdAt: number; updatedAt: number; } export type TSecretSetConsumerRolloutStatus = | 'pending' | 'published' | 'applied' | 'failed'; export interface ISecretSetConsumerRolloutSummary { rolloutId: string; secretId: string; secretVersionId: string; totalConsumers: number; pendingConsumers: number; failedConsumers: number; updatedAt: number; } export interface ISecretSetConsumerRolloutStatus { serviceId: string; status: TSecretSetConsumerRolloutStatus; desiredManifestIds: string[]; appliedManifestIds: string[]; errorCode?: string; updatedAt: number; } export interface ISecretLauncherEnvironmentDelivery { type: 'launcher-environment'; variableName: string; uid: number; gid: number; mode: 0o400 | 0o440; } export interface ISecretFileDelivery { type: 'file'; targetPath: string; uid: number; gid: number; mode: 0o400 | 0o440; } export type TSecretDelivery = ISecretLauncherEnvironmentDelivery | ISecretFileDelivery; export const secretValueLimits = Object.freeze({ maximumBytes: 500 * 1024, minimumGeneratedBytes: 16, maximumGeneratedBytes: 64, }); export type TSecretGeneratedEncoding = 'hex' | 'base64url'; export type TSecretValueInput = | { mode: 'sealed'; envelope: plugins.smartcrypto.IX25519EnvelopeV1; } | { mode: 'generated'; encoding: TSecretGeneratedEncoding; bytes: number; }; export interface ISecretSetAttachmentMapping { /** Source key in the attached SecretSet. */ sourceKey: string; /** Final service key. Defaults to sourceKey. */ targetKey?: string; /** Optional service-specific delivery override. */ delivery?: TSecretDelivery; } export interface ISecretSetAttachment { id: string; secretSetId: string; environment: TSecretEnvironment; mappings?: ISecretSetAttachmentMapping[]; } /** * Caller-managed value-free inputs to service secret resolution. * Cloudly verifies organization ownership when resolving the referenced sets. */ export interface IServiceSecretConfiguration { schemaVersion: 2; /** Dedicated CAS revision for SecretSet attachments. */ revision: number; /** Dedicated CAS revision for service-owned secret membership. */ secretsRevision: number; secretSetAttachments: ISecretSetAttachment[]; } export interface IResolvedSecretManifestEntry { secretId: string; secretVersionId: string; key: string; environment: TSecretEnvironment; owner: TWorkloadSecretOwner; delivery: TSecretDelivery; } /** Immutable, value-free runtime snapshot. */ export interface IResolvedSecretManifest { schemaVersion: 2; id: string; digest: TSha256Digest; organizationId: string; serviceId: string; clusterId: string; imageRolloutId: string; imageRolloutGeneration: number; requestedImageDigest: TSha256Digest; invocationDigest: TSha256Digest; secretRolloutId: string; secretRolloutGeneration: number; entries: IResolvedSecretManifestEntry[]; createdAt: number; } export interface IResolvedSecretManifestReference { manifestId: string; manifestDigest: TSha256Digest; clusterId: string; imageRolloutId: string; imageRolloutGeneration: number; requestedImageDigest: TSha256Digest; invocationDigest: TSha256Digest; secretRolloutId: string; secretRolloutGeneration: number; } export interface IAcceptedPreviousSecretManifest { manifest: IResolvedSecretManifestReference; /** Exclusive material-issuance deadline for tasks draining the previous rollout. */ acceptedUntil: number; } export type TClusterSecretDeploymentStatus = | 'pending' | 'applying' | 'applied' | 'drifted' | 'failed'; export interface IClusterSecretDeploymentState { clusterId: string; desiredManifest: IResolvedSecretManifestReference; appliedManifest?: IResolvedSecretManifestReference; previousAcceptedManifest?: IAcceptedPreviousSecretManifest; status: TClusterSecretDeploymentStatus; updatedAt: number; } /** Server-managed per-cluster authority switched with an expected revision CAS. */ export interface IServiceSecretDeploymentPlan { schemaVersion: 2; revision: number; clusterStates: IClusterSecretDeploymentState[]; updatedAt: number; } export const resolvedSecretManifestReferencesEqual = ( leftArg: IResolvedSecretManifestReference | undefined, rightArg: IResolvedSecretManifestReference | undefined, ): boolean => ( Boolean(leftArg) && Boolean(rightArg) && leftArg?.manifestId === rightArg?.manifestId && leftArg?.manifestDigest === rightArg?.manifestDigest && leftArg?.clusterId === rightArg?.clusterId && leftArg?.imageRolloutId === rightArg?.imageRolloutId && leftArg?.imageRolloutGeneration === rightArg?.imageRolloutGeneration && leftArg?.requestedImageDigest === rightArg?.requestedImageDigest && leftArg?.invocationDigest === rightArg?.invocationDigest && leftArg?.secretRolloutId === rightArg?.secretRolloutId && leftArg?.secretRolloutGeneration === rightArg?.secretRolloutGeneration ); export const isResolvedSecretManifestReferenceAccepted = ( stateArg: IClusterSecretDeploymentState, referenceArg: IResolvedSecretManifestReference, nowArg: number, ): boolean => ( resolvedSecretManifestReferencesEqual(stateArg.desiredManifest, referenceArg) || resolvedSecretManifestReferencesEqual(stateArg.appliedManifest, referenceArg) || ( Boolean(stateArg.previousAcceptedManifest) && nowArg < stateArg.previousAcceptedManifest!.acceptedUntil && resolvedSecretManifestReferencesEqual( stateArg.previousAcceptedManifest!.manifest, referenceArg, ) ) ); const secretIdentifierRegex = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/; const secretEnvironmentRegex = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/; const secretKeyRegex = /^[A-Z_][A-Z0-9_]{0,252}$/; const secretDigestRegex = /^sha256:[a-f0-9]{64}$/; const isRecord = (valueArg: unknown): valueArg is Record => ( Boolean(valueArg) && typeof valueArg === 'object' && !Array.isArray(valueArg) ); const hasExactKeys = ( valueArg: Record, keysArg: string[], ): boolean => { const actual = Object.keys(valueArg).sort(); const expected = [...keysArg].sort(); return JSON.stringify(actual) === JSON.stringify(expected); }; const hasOnlyKeys = ( valueArg: Record, keysArg: string[], ): boolean => Object.keys(valueArg).every((keyArg) => keysArg.includes(keyArg)); const isSecretIdentifier = (valueArg: unknown): valueArg is string => ( typeof valueArg === 'string' && secretIdentifierRegex.test(valueArg) ); const isSecretKey = (valueArg: unknown): valueArg is string => ( typeof valueArg === 'string' && secretKeyRegex.test(valueArg) ); const isSecretEnvironment = (valueArg: unknown): valueArg is string => ( typeof valueArg === 'string' && secretEnvironmentRegex.test(valueArg) ); const isNonNegativeSafeInteger = (valueArg: unknown): valueArg is number => ( Number.isSafeInteger(valueArg) && (valueArg as number) >= 0 ); const isPositiveSafeInteger = (valueArg: unknown): valueArg is number => ( Number.isSafeInteger(valueArg) && (valueArg as number) > 0 ); export const validateSecretMutationId = (mutationIdArg: unknown): string[] => ( isSecretIdentifier(mutationIdArg) ? [] : ['mutationId must be a canonical identifier'] ); export const validateSecretVersionRetentionReferences = ( referencesArg: unknown, ): string[] => { if (!Array.isArray(referencesArg) || referencesArg.length > secretVersionRetentionReferenceLimits.pageSizeMaximum) { return ['secret version retention references must be a bounded array']; } const errors: string[] = []; const kinds = new Set([ 'desired-manifest', 'applied-manifest', 'previous-accepted-manifest', 'rollback-manifest', 'backup', 'retention-window', ]); let previousSortKey: string | undefined; for (const [index, referenceArg] of referencesArg.entries()) { if (!isRecord(referenceArg) || !hasOnlyKeys(referenceArg, ['kind', 'resourceId', 'expiresAt']) || !kinds.has(referenceArg.kind as TSecretVersionRetentionReferenceKind) || !isSecretIdentifier(referenceArg.resourceId) || (referenceArg.expiresAt !== undefined && !isNonNegativeSafeInteger(referenceArg.expiresAt))) { errors.push(`secret version retention references[${index}] must be canonical`); continue; } const sortKey = `${referenceArg.kind}\0${referenceArg.resourceId}\0${ referenceArg.expiresAt === undefined ? '' : String(referenceArg.expiresAt).padStart(16, '0') }`; if (previousSortKey !== undefined && sortKey <= previousSortKey) { errors.push('secret version retention references must be uniquely sorted'); } previousSortKey = sortKey; } return errors; }; export const validateSecretVersionMetadata = ( versionArg: unknown, ): string[] => { if (!isRecord(versionArg)) return ['secret version metadata must be an object']; const errors: string[] = []; if (!hasOnlyKeys(versionArg, [ 'id', 'secretId', 'revision', 'lifecycleState', 'createdAt', 'createdBy', 'source', 'retiredAt', 'revokedAt', 'purgeOperationId', 'purgedAt', ])) { errors.push('secret version metadata contains unsupported fields'); } if (!isSecretIdentifier(versionArg.id) || !isSecretIdentifier(versionArg.secretId)) { errors.push('secret version metadata identity must be canonical'); } if (!isPositiveSafeInteger(versionArg.revision)) { errors.push('secret version metadata revision must be positive'); } if (!['active', 'retired', 'revoked', 'purge-pending', 'purged'] .includes(versionArg.lifecycleState as string)) { errors.push('secret version metadata lifecycleState must be canonical'); } if (!isNonNegativeSafeInteger(versionArg.createdAt) || !isSecretIdentifier(versionArg.createdBy) || !['operator', 'appstore', 'mail', 'oidc', 'webpush', 'platform', 'system'] .includes(versionArg.source as string)) { errors.push('secret version metadata creation fields must be canonical'); } for (const field of ['retiredAt', 'revokedAt', 'purgedAt'] as const) { if (versionArg[field] !== undefined && !isNonNegativeSafeInteger(versionArg[field])) { errors.push(`secret version metadata ${field} must be a non-negative integer`); } } const hasPurgeOperationId = isSecretIdentifier(versionArg.purgeOperationId); if (versionArg.lifecycleState === 'purge-pending') { if (!hasPurgeOperationId || versionArg.purgedAt !== undefined) { errors.push('purge-pending secret version metadata requires only a purge operation'); } } else if (versionArg.lifecycleState === 'purged') { if (!hasPurgeOperationId || !isNonNegativeSafeInteger(versionArg.purgedAt)) { errors.push('purged secret version metadata requires a purge operation and purgedAt'); } } else if (versionArg.purgeOperationId !== undefined || versionArg.purgedAt !== undefined) { errors.push('non-purging secret version metadata forbids purge fields'); } return errors; }; export const validateSecretVersionPurgeIntent = ( intentArg: unknown, ): string[] => { if (!isRecord(intentArg) || !hasExactKeys(intentArg, [ 'schemaVersion', 'mutationId', 'secretId', 'secretVersionId', 'expectedSecretRevision', 'expectedSecretVersionRevision', 'expectedTargetSecretsRevision', ])) { return ['secret version purge intent must use its exact schema']; } const errors = validateSecretMutationId(intentArg.mutationId); if (intentArg.schemaVersion !== 1) { errors.push('secret version purge intent schemaVersion must be 1'); } for (const field of ['secretId', 'secretVersionId'] as const) { if (!isSecretIdentifier(intentArg[field])) { errors.push(`secret version purge intent ${field} must be canonical`); } } for (const field of ['expectedSecretRevision', 'expectedSecretVersionRevision'] as const) { if (!isPositiveSafeInteger(intentArg[field])) { errors.push(`secret version purge intent ${field} must be positive`); } } if (!isNonNegativeSafeInteger(intentArg.expectedTargetSecretsRevision)) { errors.push('secret version purge intent expectedTargetSecretsRevision must be non-negative'); } return errors; }; export const createSecretVersionPurgeIntentDigestInput = ( intentArg: ISecretVersionPurgeIntentV1, ): string => JSON.stringify({ schemaVersion: intentArg.schemaVersion, purpose: 'serve.zone/secret-version-purge', mutationId: intentArg.mutationId, secretId: intentArg.secretId, secretVersionId: intentArg.secretVersionId, expectedSecretRevision: intentArg.expectedSecretRevision, expectedSecretVersionRevision: intentArg.expectedSecretVersionRevision, expectedTargetSecretsRevision: intentArg.expectedTargetSecretsRevision, }); export const computeSecretVersionPurgeIntentDigest = async ( intentArg: ISecretVersionPurgeIntentV1, ): Promise => { if (validateSecretVersionPurgeIntent(intentArg).length > 0) { throw new Error('secret version purge intent must be canonical'); } const digest = new Uint8Array(await globalThis.crypto.subtle.digest( 'SHA-256', new TextEncoder().encode(createSecretVersionPurgeIntentDigestInput(intentArg)), )); return `sha256:${[...digest] .map((byteArg) => byteArg.toString(16).padStart(2, '0')) .join('')}` as TSha256Digest; }; export const verifySecretVersionPurgeIntentDigest = async ( intentArg: ISecretVersionPurgeIntentV1, digestArg: unknown, ): Promise => { try { return typeof digestArg === 'string' && secretDigestRegex.test(digestArg) && digestArg === await computeSecretVersionPurgeIntentDigest(intentArg); } catch { return false; } }; export const validateSecretVersionPurgeOperation = async ( operationArg: unknown, ): Promise => { if (!isRecord(operationArg)) return ['secret version purge operation must be an object']; const state = operationArg.state; const expectedKeys = state === 'failed' ? [ 'schemaVersion', 'id', 'revision', 'intent', 'intentDigest', 'state', 'failure', 'createdAt', 'updatedAt', ] : state === 'succeeded' ? [ 'schemaVersion', 'id', 'revision', 'intent', 'intentDigest', 'state', 'purgedAt', 'createdAt', 'updatedAt', ] : [ 'schemaVersion', 'id', 'revision', 'intent', 'intentDigest', 'state', 'createdAt', 'updatedAt', ]; if (!['pending', 'erasing', 'failed', 'succeeded'].includes(state as string) || !hasExactKeys(operationArg, expectedKeys)) { return ['secret version purge operation must use its exact state schema']; } const errors = validateSecretVersionPurgeIntent(operationArg.intent); if (operationArg.schemaVersion !== 1) { errors.push('secret version purge operation schemaVersion must be 1'); } if (!isSecretIdentifier(operationArg.id) || !isPositiveSafeInteger(operationArg.revision)) { errors.push('secret version purge operation identity and revision must be canonical'); } if (!isNonNegativeSafeInteger(operationArg.createdAt) || !isNonNegativeSafeInteger(operationArg.updatedAt) || (operationArg.updatedAt as number) < (operationArg.createdAt as number)) { errors.push('secret version purge operation timestamps must be ordered'); } if (isRecord(operationArg.intent) && typeof operationArg.intentDigest === 'string' && secretDigestRegex.test(operationArg.intentDigest)) { try { if (!await verifySecretVersionPurgeIntentDigest( operationArg.intent as unknown as ISecretVersionPurgeIntentV1, operationArg.intentDigest, )) { errors.push('secret version purge operation intentDigest does not match'); } } catch { errors.push('secret version purge operation intentDigest cannot be verified'); } } else { errors.push('secret version purge operation intentDigest must be canonical'); } if (state === 'failed') { if (!isRecord(operationArg.failure) || !hasExactKeys(operationArg.failure, ['code', 'retryable', 'failedAt']) || typeof operationArg.failure.code !== 'string' || !/^[A-Z][A-Z0-9_]{0,127}$/.test(operationArg.failure.code) || typeof operationArg.failure.retryable !== 'boolean' || !isNonNegativeSafeInteger(operationArg.failure.failedAt) || (operationArg.failure.failedAt as number) < (operationArg.createdAt as number) || (operationArg.failure.failedAt as number) > (operationArg.updatedAt as number)) { errors.push('secret version purge operation failure must be canonical'); } } if (state === 'succeeded' && (!isNonNegativeSafeInteger(operationArg.purgedAt) || (operationArg.purgedAt as number) < (operationArg.createdAt as number) || (operationArg.purgedAt as number) > (operationArg.updatedAt as number))) { errors.push('succeeded secret version purge operation requires a canonical purgedAt'); } return errors; }; export const validateSecretOwner = (ownerArg: unknown): string[] => { if (!isRecord(ownerArg)) return ['secret owner must be an object']; const shapeByKind = { service: ['kind', 'serviceId'], 'secret-set': ['kind', 'secretSetId'], 'platform-provider': ['kind', 'providerConfigId'], system: ['kind', 'systemId'], } as const; const keys = shapeByKind[ownerArg.kind as keyof typeof shapeByKind]; if (!keys || !hasExactKeys(ownerArg, [...keys])) { return ['secret owner must use an exact supported owner schema']; } const idField = keys[1]; return isSecretIdentifier(ownerArg[idField]) ? [] : [`secret owner ${idField} must be canonical`]; }; export const validateSecretManagementScope = ( sourceArg: TSecretManagementSource, scopeArg: unknown, ): string[] => { if (scopeArg === 'operator') { return sourceArg === 'operator' ? [] : ['operator management scope requires operator source']; } if (typeof scopeArg !== 'string') { return ['management scope must be canonical']; } const separatorIndex = scopeArg.indexOf(':'); const scopeSource = scopeArg.slice(0, separatorIndex); const scopeIdentifier = scopeArg.slice(separatorIndex + 1); if ( separatorIndex < 1 || !['appstore', 'mail', 'oidc', 'webpush', 'platform', 'system'].includes(scopeSource) || !isSecretIdentifier(scopeIdentifier) ) { return ['management scope must be canonical']; } return scopeSource === sourceArg ? [] : ['management scope prefix must match management source']; }; export const validateSecretDelivery = (deliveryArg: unknown): string[] => { if (!isRecord(deliveryArg)) return ['delivery must be an object']; const isLauncher = deliveryArg.type === 'launcher-environment'; if (!isLauncher && deliveryArg.type !== 'file') { return ['delivery type must be launcher-environment or file']; } const expectedKeys = isLauncher ? ['type', 'variableName', 'uid', 'gid', 'mode'] : ['type', 'targetPath', 'uid', 'gid', 'mode']; if (!hasExactKeys(deliveryArg, expectedKeys)) { return ['delivery must use its exact schema']; } const errors: string[] = []; if (isLauncher && !isSecretKey(deliveryArg.variableName)) { errors.push('launcher delivery variableName must be a canonical environment key'); } if (!isLauncher && (typeof deliveryArg.targetPath !== 'string' || normalizeServiceAbsolutePath(deliveryArg.targetPath) !== deliveryArg.targetPath || !/^\/run\/secrets\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(deliveryArg.targetPath))) { errors.push('file delivery targetPath must be a canonical file below /run/secrets'); } for (const field of ['uid', 'gid'] as const) { const value = deliveryArg[field]; if (!Number.isSafeInteger(value) || (value as number) < 0 || (value as number) > 2_147_483_647) { errors.push(`${isLauncher ? 'launcher' : 'file'} delivery ${field} must be a bounded numeric identity`); } } if (deliveryArg.mode !== 0o400 && deliveryArg.mode !== 0o440) { errors.push(`${isLauncher ? 'launcher' : 'file'} delivery mode must be 0400 or 0440`); } return errors; }; export const validateSecretMetadata = (secretArg: unknown): string[] => { if (!isRecord(secretArg)) return ['secret metadata must be an object']; const expectedKeys = [ 'id', 'organizationId', 'owner', 'key', 'environment', 'name', ...(secretArg.description === undefined ? [] : ['description']), 'tags', 'managementSource', 'managementScope', 'defaultDelivery', 'lifecycleState', ...(secretArg.activeVersion === undefined ? [] : ['activeVersion']), 'versionCount', 'revision', 'createdAt', 'updatedAt', ...(secretArg.lastRotatedAt === undefined ? [] : ['lastRotatedAt']), ]; const errors: string[] = []; if (!hasExactKeys(secretArg, expectedKeys)) { errors.push('secret metadata must use its exact schema'); } if (!isSecretIdentifier(secretArg.id) || !isSecretIdentifier(secretArg.organizationId)) { errors.push('secret metadata identity must be canonical'); } errors.push(...validateSecretOwner(secretArg.owner)); if (!isSecretKey(secretArg.key) || !isSecretEnvironment(secretArg.environment)) { errors.push('secret metadata key and environment must be canonical'); } if (typeof secretArg.name !== 'string' || secretArg.name.trim() !== secretArg.name || secretArg.name.length < 1 || secretArg.name.length > 200 || (secretArg.description !== undefined && (typeof secretArg.description !== 'string' || secretArg.description.length > 2000))) { errors.push('secret metadata name and description must be bounded'); } if (!Array.isArray(secretArg.tags) || secretArg.tags.length > 64) { errors.push('secret metadata tags must be a bounded array'); } else { const tagKeys: string[] = []; for (const [index, tagArg] of secretArg.tags.entries()) { if (!isRecord(tagArg) || !hasExactKeys(tagArg, ['key', 'value']) || typeof tagArg.key !== 'string' || tagArg.key.length < 1 || tagArg.key.length > 100 || typeof tagArg.value !== 'string' || tagArg.value.length > 500) { errors.push(`secret metadata tags[${index}] must use the bounded key/value schema`); } else { tagKeys.push(tagArg.key); } } if (new Set(tagKeys).size !== tagKeys.length) { errors.push('secret metadata tag keys must be unique'); } } if (!['operator', 'appstore', 'mail', 'oidc', 'webpush', 'platform', 'system'] .includes(secretArg.managementSource as string)) { errors.push('secret metadata managementSource must be canonical'); } else { errors.push(...validateSecretManagementScope( secretArg.managementSource as TSecretManagementSource, secretArg.managementScope, )); } errors.push(...validateSecretDelivery(secretArg.defaultDelivery)); if (!['active', 'retired', 'revoked', 'deleted'].includes(secretArg.lifecycleState as string)) { errors.push('secret metadata lifecycleState must be canonical'); } if (secretArg.activeVersion !== undefined) { if (!isRecord(secretArg.activeVersion) || !hasExactKeys(secretArg.activeVersion, ['secretId', 'secretVersionId']) || secretArg.activeVersion.secretId !== secretArg.id || !isSecretIdentifier(secretArg.activeVersion.secretVersionId)) { errors.push('secret metadata activeVersion must be a canonical self-reference'); } } if (!isNonNegativeSafeInteger(secretArg.versionCount) || !isPositiveSafeInteger(secretArg.revision) || !isNonNegativeSafeInteger(secretArg.createdAt) || !isNonNegativeSafeInteger(secretArg.updatedAt) || (secretArg.updatedAt as number) < (secretArg.createdAt as number)) { errors.push('secret metadata counts, revision, and timestamps must be canonical'); } if (secretArg.lastRotatedAt !== undefined && (!isNonNegativeSafeInteger(secretArg.lastRotatedAt) || (secretArg.lastRotatedAt as number) < (secretArg.createdAt as number) || (secretArg.lastRotatedAt as number) > (secretArg.updatedAt as number))) { errors.push('secret metadata lastRotatedAt must be within its lifetime'); } return errors; }; export const validateSecretValueInput = (valueInputArg: unknown): string[] => { if (!isRecord(valueInputArg)) return ['valueInput must be an object']; if (valueInputArg.mode === 'sealed') { if (!hasExactKeys(valueInputArg, ['mode', 'envelope'])) { return ['sealed valueInput must use its exact schema']; } try { const envelope = plugins.smartcrypto.parseX25519Envelope(valueInputArg.envelope); return envelope.ciphertext.length <= Math.ceil(secretValueLimits.maximumBytes * 4 / 3) ? [] : [`sealed secret material must not exceed ${secretValueLimits.maximumBytes} bytes`]; } catch { return ['sealed valueInput envelope must be a strict X25519 envelope']; } } if (valueInputArg.mode === 'generated') { if (!hasExactKeys(valueInputArg, ['mode', 'encoding', 'bytes'])) { return ['generated valueInput must use its exact schema']; } const errors: string[] = []; if (valueInputArg.encoding !== 'hex' && valueInputArg.encoding !== 'base64url') { errors.push('generated valueInput encoding must be hex or base64url'); } if (!Number.isSafeInteger(valueInputArg.bytes) || (valueInputArg.bytes as number) < secretValueLimits.minimumGeneratedBytes || (valueInputArg.bytes as number) > secretValueLimits.maximumGeneratedBytes) { errors.push('generated valueInput bytes are outside the supported bounds'); } return errors; } return ['valueInput mode must be sealed or generated']; }; const encodeBase64Url = (bytesArg: Uint8Array): string => { let binary = ''; for (const byte of bytesArg) binary += String.fromCharCode(byte); return globalThis.btoa(binary) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); }; /** Checks the SmartCrypto context digest without opening or decrypting the envelope. */ export const verifySecretEnvelopeContext = async ( envelopeArg: unknown, contextArg: unknown, ): Promise => { if (!(contextArg instanceof Uint8Array)) return false; let envelope: plugins.smartcrypto.IX25519EnvelopeV1; try { envelope = plugins.smartcrypto.parseX25519Envelope(envelopeArg); } catch { return false; } const contextDigest = new Uint8Array(await globalThis.crypto.subtle.digest( 'SHA-256', new Uint8Array(contextArg), )); return envelope.contextDigest === encodeBase64Url(contextDigest); }; /** Files accept arbitrary bytes; launcher values must be valid UTF-8 without NUL. */ export const validateSecretValueBytes = ( valueBytesArg: unknown, deliveryArg: TSecretDelivery, ): string[] => { if (!(valueBytesArg instanceof Uint8Array)) return ['secret material must be bytes']; if (valueBytesArg.byteLength > secretValueLimits.maximumBytes) { return [`secret material must not exceed ${secretValueLimits.maximumBytes} bytes`]; } if (deliveryArg.type === 'file') return []; try { const decoded = new TextDecoder('utf-8', { fatal: true }).decode(valueBytesArg); return decoded.includes('\0') ? ['launcher secret material must not contain NUL'] : []; } catch { return ['launcher secret material must be valid UTF-8']; } }; /** * One shared precedence rule for resolution and runtime verification. * A manifest is authoritative once published; before publication an attachment * override wins over the Secret default. */ export const resolveSecretDelivery = (deliveryArg: { secretDefault: TSecretDelivery; attachmentOverride?: TSecretDelivery; manifestDelivery?: TSecretDelivery; }): TSecretDelivery => ( deliveryArg.manifestDelivery ?? deliveryArg.attachmentOverride ?? deliveryArg.secretDefault ); export const validateServiceSecretConfiguration = ( configurationArg: unknown, ): string[] => { if (!isRecord(configurationArg)) { return ['secretConfiguration must be an object']; } const errors: string[] = []; if (!hasExactKeys(configurationArg, [ 'schemaVersion', 'revision', 'secretsRevision', 'secretSetAttachments', ])) { errors.push('secretConfiguration must use its exact schema'); } if (configurationArg.schemaVersion !== 2) { errors.push('secretConfiguration schemaVersion must be 2'); } if (!Array.isArray(configurationArg.secretSetAttachments)) { errors.push('secretSetAttachments must be an array'); return errors; } if (configurationArg.secretSetAttachments.length > 64) { errors.push('secretSetAttachments must contain at most 64 entries'); return errors; } if (!Number.isSafeInteger(configurationArg.revision) || (configurationArg.revision as number) < 0) { errors.push('secretConfiguration revision must be a non-negative integer'); } if (!Number.isSafeInteger(configurationArg.secretsRevision) || (configurationArg.secretsRevision as number) < 0) { errors.push('secretConfiguration secretsRevision must be a non-negative integer'); } const attachmentIds: string[] = []; const setEnvironmentKeys: string[] = []; for (const [attachmentIndex, candidate] of configurationArg.secretSetAttachments.entries()) { if (!isRecord(candidate)) { errors.push(`secretSetAttachments[${attachmentIndex}] must be an object`); continue; } if (!hasOnlyKeys(candidate, ['id', 'secretSetId', 'environment', 'mappings'])) { errors.push(`secretSetAttachments[${attachmentIndex}] contains unsupported fields`); } if (!isSecretIdentifier(candidate.id)) { errors.push(`secretSetAttachments[${attachmentIndex}].id must be a canonical identifier`); } else { attachmentIds.push(candidate.id); } if (!isSecretIdentifier(candidate.secretSetId)) { errors.push( `secretSetAttachments[${attachmentIndex}].secretSetId must be a canonical identifier`, ); } if (!isSecretEnvironment(candidate.environment)) { errors.push( `secretSetAttachments[${attachmentIndex}].environment must be a canonical environment`, ); } if (isSecretIdentifier(candidate.secretSetId) && isSecretEnvironment(candidate.environment)) { setEnvironmentKeys.push(`${candidate.secretSetId}\0${candidate.environment}`); } if (candidate.mappings === undefined) continue; if (!Array.isArray(candidate.mappings) || candidate.mappings.length > 128) { errors.push(`secretSetAttachments[${attachmentIndex}].mappings must be a bounded array`); continue; } const sourceKeys: string[] = []; const targetKeys: string[] = []; for (const [mappingIndex, mappingCandidate] of candidate.mappings.entries()) { if (!isRecord(mappingCandidate)) { errors.push( `secretSetAttachments[${attachmentIndex}].mappings[${mappingIndex}] must be an object`, ); continue; } if (!hasOnlyKeys(mappingCandidate, ['sourceKey', 'targetKey', 'delivery'])) { errors.push( `secretSetAttachments[${attachmentIndex}].mappings[${mappingIndex}] contains unsupported fields`, ); } if (!isSecretKey(mappingCandidate.sourceKey)) { errors.push( `secretSetAttachments[${attachmentIndex}].mappings[${mappingIndex}].sourceKey must be canonical`, ); } else { sourceKeys.push(mappingCandidate.sourceKey); } const finalKey = mappingCandidate.targetKey ?? mappingCandidate.sourceKey; if (!isSecretKey(finalKey)) { errors.push( `secretSetAttachments[${attachmentIndex}].mappings[${mappingIndex}].targetKey must be canonical`, ); } else { targetKeys.push(finalKey); } if (mappingCandidate.delivery !== undefined) { errors.push(...validateSecretDelivery(mappingCandidate.delivery).map( (errorArg) => ( `secretSetAttachments[${attachmentIndex}].mappings[${mappingIndex}].${errorArg}` ), )); } } if (new Set(sourceKeys).size !== sourceKeys.length) { errors.push(`secretSetAttachments[${attachmentIndex}] has duplicate source mappings`); } if (new Set(targetKeys).size !== targetKeys.length) { errors.push(`secretSetAttachments[${attachmentIndex}] has duplicate target mappings`); } } if (new Set(attachmentIds).size !== attachmentIds.length) { errors.push('secretSetAttachments ids must be unique'); } if (new Set(setEnvironmentKeys).size !== setEnvironmentKeys.length) { errors.push('a SecretSet may be attached only once per environment'); } return errors; }; export const validateSecretSetAttachmentOwnership = ( configurationArg: IServiceSecretConfiguration, serviceOrganizationIdArg: string, secretSetsArg: ISecretSetMetadata[], ): string[] => { const errors = validateServiceSecretConfiguration(configurationArg); const secretSetsById = new Map(secretSetsArg.map((secretSetArg) => [ secretSetArg.id, secretSetArg, ])); for (const attachment of configurationArg.secretSetAttachments || []) { const secretSet = secretSetsById.get(attachment.secretSetId); if (!secretSet) { errors.push(`SecretSet ${attachment.secretSetId} does not exist`); } else if (secretSet.organizationId !== serviceOrganizationIdArg) { errors.push(`SecretSet ${attachment.secretSetId} belongs to another organization`); } else if (secretSet.lifecycleState !== 'active') { errors.push(`SecretSet ${attachment.secretSetId} is not active`); } } return errors; }; export const createResolvedSecretManifestDigestInput = ( manifestArg: IResolvedSecretManifest, ): string => JSON.stringify({ schemaVersion: manifestArg.schemaVersion, id: manifestArg.id, organizationId: manifestArg.organizationId, serviceId: manifestArg.serviceId, clusterId: manifestArg.clusterId, imageRolloutId: manifestArg.imageRolloutId, imageRolloutGeneration: manifestArg.imageRolloutGeneration, requestedImageDigest: manifestArg.requestedImageDigest, invocationDigest: manifestArg.invocationDigest, secretRolloutId: manifestArg.secretRolloutId, secretRolloutGeneration: manifestArg.secretRolloutGeneration, entries: manifestArg.entries.map((entryArg) => ({ secretId: entryArg.secretId, secretVersionId: entryArg.secretVersionId, key: entryArg.key, environment: entryArg.environment, owner: entryArg.owner.kind === 'service' ? { kind: 'service', serviceId: entryArg.owner.serviceId, } : { kind: 'secret-set', secretSetId: entryArg.owner.secretSetId, }, delivery: entryArg.delivery.type === 'launcher-environment' ? { type: 'launcher-environment', variableName: entryArg.delivery.variableName, uid: entryArg.delivery.uid, gid: entryArg.delivery.gid, mode: entryArg.delivery.mode, } : { type: 'file', targetPath: entryArg.delivery.targetPath, uid: entryArg.delivery.uid, gid: entryArg.delivery.gid, mode: entryArg.delivery.mode, }, })), createdAt: manifestArg.createdAt, }); /** Shared canonical digest; the digest field itself is excluded from the input. */ export const computeResolvedSecretManifestDigest = async ( manifestArg: IResolvedSecretManifest, ): Promise => { const encoded = new TextEncoder().encode(createResolvedSecretManifestDigestInput(manifestArg)); const digestBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoded); const digestHex = [...new Uint8Array(digestBuffer)] .map((byteArg) => byteArg.toString(16).padStart(2, '0')) .join(''); return `sha256:${digestHex}` as TSha256Digest; }; export const verifyResolvedSecretManifestDigest = async ( manifestArg: IResolvedSecretManifest, ): Promise => ( manifestArg.digest === await computeResolvedSecretManifestDigest(manifestArg) ); export const validateResolvedSecretManifest = ( manifestArg: unknown, ): string[] => { if (!isRecord(manifestArg)) return ['secret manifest must be an object']; const errors: string[] = []; if (!hasExactKeys(manifestArg, [ 'schemaVersion', 'id', 'digest', 'organizationId', 'serviceId', 'clusterId', 'imageRolloutId', 'imageRolloutGeneration', 'requestedImageDigest', 'invocationDigest', 'secretRolloutId', 'secretRolloutGeneration', 'entries', 'createdAt', ])) { errors.push('secret manifest must use its exact schema'); } if (manifestArg.schemaVersion !== 2) errors.push('secret manifest schemaVersion must be 2'); for (const field of [ 'id', 'organizationId', 'serviceId', 'clusterId', 'imageRolloutId', 'secretRolloutId', ] as const) { if (!isSecretIdentifier(manifestArg[field])) { errors.push(`secret manifest ${field} must be a canonical identifier`); } } if (typeof manifestArg.digest !== 'string' || !secretDigestRegex.test(manifestArg.digest)) { errors.push('secret manifest digest must be a canonical sha256 digest'); } for (const field of ['requestedImageDigest', 'invocationDigest'] as const) { if (typeof manifestArg[field] !== 'string' || !secretDigestRegex.test(manifestArg[field])) { errors.push(`secret manifest ${field} must be a canonical sha256 digest`); } } if (!Number.isSafeInteger(manifestArg.imageRolloutGeneration) || (manifestArg.imageRolloutGeneration as number) < 1) { errors.push('secret manifest image rollout generation must be a positive integer'); } if (!Number.isSafeInteger(manifestArg.secretRolloutGeneration) || (manifestArg.secretRolloutGeneration as number) < 1) { errors.push('secret manifest rollout generation must be a positive integer'); } if (!Number.isSafeInteger(manifestArg.createdAt) || (manifestArg.createdAt as number) < 0) { errors.push('secret manifest createdAt must be a non-negative integer'); } if (!Array.isArray(manifestArg.entries) || manifestArg.entries.length > 256) { errors.push('secret manifest entries must be a bounded array'); return errors; } const keyScopes: string[] = []; const secretIds: string[] = []; const versionIds: string[] = []; const environmentDeliveryTargets: string[] = []; const fileDeliveryTargets: string[] = []; let previousSortKey: string | undefined; for (const [index, candidate] of manifestArg.entries.entries()) { if (!isRecord(candidate)) { errors.push(`secret manifest entries[${index}] must be an object`); continue; } if (!hasExactKeys(candidate, [ 'secretId', 'secretVersionId', 'key', 'environment', 'owner', 'delivery', ])) { errors.push(`secret manifest entries[${index}] must use its exact schema`); } if (!isSecretIdentifier(candidate.secretId)) { errors.push(`secret manifest entries[${index}].secretId must be canonical`); } else { secretIds.push(candidate.secretId); } if (!isSecretIdentifier(candidate.secretVersionId)) { errors.push(`secret manifest entries[${index}].secretVersionId must be canonical`); } else { versionIds.push(candidate.secretVersionId); } if (!isSecretKey(candidate.key)) { errors.push(`secret manifest entries[${index}].key must be canonical`); } if (!isSecretEnvironment(candidate.environment)) { errors.push(`secret manifest entries[${index}].environment must be canonical`); } else if (isSecretKey(candidate.key)) { keyScopes.push(`${candidate.environment}\0${candidate.key}`); } if (!isRecord(candidate.owner) || ( candidate.owner.kind !== 'service' && candidate.owner.kind !== 'secret-set' ) || !hasExactKeys( candidate.owner, candidate.owner.kind === 'service' ? ['kind', 'serviceId'] : ['kind', 'secretSetId'], ) || ( candidate.owner.kind === 'service' && !isSecretIdentifier(candidate.owner.serviceId) ) || ( candidate.owner.kind === 'secret-set' && !isSecretIdentifier(candidate.owner.secretSetId) )) { errors.push(`secret manifest entries[${index}].owner must be canonical`); } else if ( candidate.owner.kind === 'service' && isSecretIdentifier(manifestArg.serviceId) && candidate.owner.serviceId !== manifestArg.serviceId ) { errors.push( `secret manifest entries[${index}].owner serviceId must match the manifest serviceId`, ); } errors.push(...validateSecretDelivery(candidate.delivery).map( (errorArg) => `secret manifest entries[${index}].${errorArg}`, )); if (isRecord(candidate.delivery)) { if ( candidate.delivery.type === 'launcher-environment' && isSecretKey(candidate.delivery.variableName) ) { environmentDeliveryTargets.push(candidate.delivery.variableName); } else if ( candidate.delivery.type === 'file' && typeof candidate.delivery.targetPath === 'string' && validateSecretDelivery(candidate.delivery).length === 0 ) { fileDeliveryTargets.push(candidate.delivery.targetPath); } } if (isSecretKey(candidate.key) && isSecretEnvironment(candidate.environment)) { const sortKey = `${candidate.environment}\0${candidate.key}`; if (previousSortKey !== undefined && sortKey <= previousSortKey) { errors.push('secret manifest entries must be uniquely sorted by environment and key'); } previousSortKey = sortKey; } } if (new Set(keyScopes).size !== keyScopes.length) { errors.push('secret manifest final keys must be unique per environment'); } if (new Set(secretIds).size !== secretIds.length) { errors.push('secret manifest logical secret references must be unique'); } if (new Set(versionIds).size !== versionIds.length) { errors.push('secret manifest version references must be unique'); } if (new Set(environmentDeliveryTargets).size !== environmentDeliveryTargets.length) { errors.push('secret manifest environment delivery targets must be globally unique'); } if (new Set(fileDeliveryTargets).size !== fileDeliveryTargets.length) { errors.push('secret manifest file delivery targets must be globally unique'); } return errors; }; const validateResolvedSecretManifestReferenceAtPath = ( referenceArg: unknown, expectedClusterIdArg: string, pathArg: string, ): string[] => { if (!isRecord(referenceArg) || !hasExactKeys(referenceArg, [ 'manifestId', 'manifestDigest', 'clusterId', 'imageRolloutId', 'imageRolloutGeneration', 'requestedImageDigest', 'invocationDigest', 'secretRolloutId', 'secretRolloutGeneration', ]) || !isSecretIdentifier(referenceArg.manifestId) || typeof referenceArg.manifestDigest !== 'string' || !secretDigestRegex.test(referenceArg.manifestDigest) || referenceArg.clusterId !== expectedClusterIdArg || !isSecretIdentifier(referenceArg.imageRolloutId) || !Number.isSafeInteger(referenceArg.imageRolloutGeneration) || (referenceArg.imageRolloutGeneration as number) < 1 || typeof referenceArg.requestedImageDigest !== 'string' || !secretDigestRegex.test(referenceArg.requestedImageDigest) || typeof referenceArg.invocationDigest !== 'string' || !secretDigestRegex.test(referenceArg.invocationDigest) || !isSecretIdentifier(referenceArg.secretRolloutId) || !Number.isSafeInteger(referenceArg.secretRolloutGeneration) || (referenceArg.secretRolloutGeneration as number) < 1) { return [`${pathArg} must be canonical and match its cluster`]; } return []; }; /** Strict public validator for a cluster-scoped resolved manifest reference. */ export const validateResolvedSecretManifestReference = ( referenceArg: unknown, expectedClusterIdArg: string, ): string[] => { try { if (!isSecretIdentifier(expectedClusterIdArg)) { return ['expected cluster id must be canonical']; } return validateResolvedSecretManifestReferenceAtPath( referenceArg, expectedClusterIdArg, 'resolved secret manifest reference', ); } catch { return ['resolved secret manifest reference must be safely inspectable']; } }; export const validateServiceSecretDeploymentPlan = ( planArg: unknown, expectedTargetClusterIdsArg?: string[], ): string[] => { if (!isRecord(planArg)) return ['secret deployment plan must be an object']; const errors: string[] = []; if (!hasExactKeys(planArg, [ 'schemaVersion', 'revision', 'clusterStates', 'updatedAt', ])) { errors.push('secret deployment plan must use its exact schema'); } if (planArg.schemaVersion !== 2) errors.push('secret deployment plan schemaVersion must be 2'); if (!Number.isSafeInteger(planArg.revision) || (planArg.revision as number) < 1) { errors.push('secret deployment plan revision must be a positive integer'); } if (!Number.isSafeInteger(planArg.updatedAt) || (planArg.updatedAt as number) < 0) { errors.push('secret deployment plan updatedAt must be a non-negative integer'); } if (!Array.isArray(planArg.clusterStates) || planArg.clusterStates.length === 0 || planArg.clusterStates.length > 128) { errors.push('secret deployment plan clusterStates must be a non-empty bounded array'); return errors; } const clusterIds: string[] = []; const manifestReferencesById = new Map(); const validateAndTrackManifestReference = ( referenceArg: unknown, expectedClusterIdArg: string, pathArg: string, ): void => { const referenceErrors = validateResolvedSecretManifestReferenceAtPath( referenceArg, expectedClusterIdArg, pathArg, ); errors.push(...referenceErrors); if (referenceErrors.length > 0) return; const reference = referenceArg as IResolvedSecretManifestReference; const existingReference = manifestReferencesById.get(reference.manifestId); if ( existingReference && !resolvedSecretManifestReferencesEqual(existingReference, reference) ) { errors.push( `${pathArg}.manifestId must identify one immutable manifest reference throughout the plan`, ); return; } manifestReferencesById.set(reference.manifestId, reference); }; for (const [index, candidate] of planArg.clusterStates.entries()) { const path = `secret deployment plan clusterStates[${index}]`; if (!isRecord(candidate) || !isSecretIdentifier(candidate.clusterId)) { errors.push(`${path}.clusterId must be canonical`); continue; } if (!hasOnlyKeys(candidate, [ 'clusterId', 'desiredManifest', 'appliedManifest', 'previousAcceptedManifest', 'status', 'updatedAt', ])) { errors.push(`${path} contains unsupported fields`); } clusterIds.push(candidate.clusterId); validateAndTrackManifestReference( candidate.desiredManifest, candidate.clusterId, `${path}.desiredManifest`, ); if (candidate.appliedManifest !== undefined) { validateAndTrackManifestReference( candidate.appliedManifest, candidate.clusterId, `${path}.appliedManifest`, ); } if (candidate.previousAcceptedManifest !== undefined) { if (!isRecord(candidate.previousAcceptedManifest) || !hasExactKeys(candidate.previousAcceptedManifest, ['manifest', 'acceptedUntil']) || !Number.isSafeInteger(candidate.previousAcceptedManifest.acceptedUntil) || (candidate.previousAcceptedManifest.acceptedUntil as number) < 0) { errors.push(`${path}.previousAcceptedManifest acceptedUntil must be non-negative`); } else { validateAndTrackManifestReference( candidate.previousAcceptedManifest.manifest, candidate.clusterId, `${path}.previousAcceptedManifest.manifest`, ); if ( Number.isSafeInteger(candidate.updatedAt) && (candidate.previousAcceptedManifest.acceptedUntil as number) < (candidate.updatedAt as number) ) { errors.push(`${path}.previousAcceptedManifest acceptedUntil predates its state`); } } } if (![ 'pending', 'applying', 'applied', 'drifted', 'failed', ].includes(candidate.status as string)) { errors.push(`${path}.status must be canonical`); } if (!Number.isSafeInteger(candidate.updatedAt) || (candidate.updatedAt as number) < 0) { errors.push(`${path}.updatedAt must be a non-negative integer`); } if ( candidate.status === 'applied' && !resolvedSecretManifestReferencesEqual( candidate.appliedManifest as unknown as IResolvedSecretManifestReference, candidate.desiredManifest as unknown as IResolvedSecretManifestReference, ) ) { errors.push(`${path} applied status requires the desired manifest to be applied`); } } if (new Set(clusterIds).size !== clusterIds.length) { errors.push('secret deployment plan clusterStates must contain each cluster once'); } if (expectedTargetClusterIdsArg) { const expected = [...new Set(expectedTargetClusterIdsArg)].sort(); if (JSON.stringify([...clusterIds].sort()) !== JSON.stringify(expected)) { errors.push('secret deployment plan target clusters do not match the service rollout scope'); } } return errors; }; export const dockerSecretResourceNameDomain = 'serve.zone/docker-secret-resource/v1' as const; export const workloadInitEnvironmentMapPath = '/run/serve.zone/workloadinit-map-v1.json' as const; export const workloadRuntimeAssetRoot = '/opt/serve.zone/runtime-assets' as const; export const workloadInitExecutablePath = '/opt/serve.zone/runtime-assets/workloadinit/v1/workloadinit' as const; export const workloadInitEnvironmentMapMode = 0o444 as const; export const workloadRuntimeAssetsReadOnly = true as const; const encodeLowercaseBase32 = (bytesArg: Uint8Array): string => { const alphabet = 'abcdefghijklmnopqrstuvwxyz234567'; let output = ''; let accumulator = 0; let bitCount = 0; for (const byte of bytesArg) { accumulator = (accumulator << 8) | byte; bitCount += 8; while (bitCount >= 5) { bitCount -= 5; output += alphabet[(accumulator >>> bitCount) & 31]; } accumulator &= (1 << bitCount) - 1; } if (bitCount > 0) output += alphabet[(accumulator << (5 - bitCount)) & 31]; return output; }; /** Stable resource name; no logical key or value material appears in Docker. */ export const createDockerSecretResourceName = async ( secretVersionIdArg: string, ): Promise<`szsv-${string}`> => { if (!isSecretIdentifier(secretVersionIdArg)) { throw new Error('secretVersionId must be a canonical identifier'); } const digest = new Uint8Array(await globalThis.crypto.subtle.digest( 'SHA-256', new TextEncoder().encode(`${dockerSecretResourceNameDomain}\0${secretVersionIdArg}`), )); const encoded = encodeLowercaseBase32(digest); if (encoded.length !== 52) throw new Error('unexpected SHA-256 base32 length'); return `szsv-${encoded}`; }; export const createLauncherSecretSourcePath = ( resourceArg: string, ): `/run/serve.zone/secrets/${string}` => { if (!/^szsv-[a-z2-7]{52}$/.test(resourceArg)) { throw new Error('resource must be a canonical serve.zone secret resource name'); } return `/run/serve.zone/secrets/${resourceArg}`; }; export interface IWorkloadInitEnvironmentMapEntryV1 { variableName: string; resource: string; source: string; } export interface IWorkloadInitEnvironmentMapV1 { schemaVersion: 1; manifestId: string; manifestDigest: TSha256Digest; entries: IWorkloadInitEnvironmentMapEntryV1[]; } /** * The nonsecret map is generated only from launcher deliveries. Direct file * deliveries mount their version resource at the declared target without the * launcher. Runtime assets are mounted read-only; no shell, symlink, or * aggregate material file participates in delivery. */ export const createWorkloadInitEnvironmentMap = async ( manifestArg: IResolvedSecretManifest, ): Promise => { if (validateResolvedSecretManifest(manifestArg).length > 0 || !await verifyResolvedSecretManifestDigest(manifestArg)) { throw new Error('workload init map requires a valid digest-verified manifest'); } const entries = await Promise.all(manifestArg.entries .filter((entryArg) => entryArg.delivery.type === 'launcher-environment') .map(async (entryArg) => { const resource = await createDockerSecretResourceName(entryArg.secretVersionId); return { variableName: (entryArg.delivery as ISecretLauncherEnvironmentDelivery).variableName, resource, source: createLauncherSecretSourcePath(resource), }; })); if (entries.length === 0) { throw new Error('workload init map requires at least one launcher-environment delivery'); } entries.sort((leftArg, rightArg) => ( leftArg.variableName < rightArg.variableName ? -1 : leftArg.variableName > rightArg.variableName ? 1 : 0 )); if (new Set(entries.map((entryArg) => entryArg.variableName)).size !== entries.length || new Set(entries.map((entryArg) => entryArg.resource)).size !== entries.length || new Set(entries.map((entryArg) => entryArg.source)).size !== entries.length) { throw new Error('workload init map entries must have unique variable, resource, and source fields'); } return { schemaVersion: 1, manifestId: manifestArg.id, manifestDigest: manifestArg.digest, entries, }; }; export const verifyWorkloadInitEnvironmentMap = async ( mapArg: unknown, manifestArg: IResolvedSecretManifest, ): Promise => { if (!isRecord(mapArg) || !hasExactKeys(mapArg, ['schemaVersion', 'manifestId', 'manifestDigest', 'entries'])) { return false; } try { const expected = await createWorkloadInitEnvironmentMap(manifestArg); return JSON.stringify(mapArg) === JSON.stringify(expected); } catch { return false; } }; export interface IWorkloadInitWrappedInvocation { command: [typeof workloadInitExecutablePath]; args: string[]; } export const createWorkloadInitWrappedInvocation = ( originalArgvArg: string[], ): IWorkloadInitWrappedInvocation => { if (!Array.isArray(originalArgvArg) || originalArgvArg.length === 0 || originalArgvArg.some((argumentArg) => typeof argumentArg !== 'string' || argumentArg.length === 0 || argumentArg.includes('\0'))) { throw new Error('original argv must be a non-empty canonical argument vector'); } return { command: [workloadInitExecutablePath], args: ['run', '--map', workloadInitEnvironmentMapPath, '--', ...originalArgvArg], }; }; /** * A Docker secret resource and its nonsecret map remain while any desired, * applied, or previous-accepted retained manifest references them. Deletion is * allowed only after no retained reference exists and Docker confirms the * corresponding rollout or service removal. */ export interface ISecretRuntimeResourceRetention { resource: string; retainedManifestIds: string[]; dockerRemovalConfirmed: boolean; } export type TSecretRecipientLifecycleState = 'active' | 'retiring' | 'revoked'; interface ISecretRecipientMetadataBase { schemaVersion: 1; recipientKeyId: string; /** Canonical unpadded base64url raw 32-byte X25519 public key. */ publicKey: string; generation: number; activatedAt: number; } export interface IActiveSecretRecipientMetadata extends ISecretRecipientMetadataBase { lifecycleState: 'active'; acceptUntil?: never; revokedAt?: never; } export interface IRetiringSecretRecipientMetadata extends ISecretRecipientMetadataBase { lifecycleState: 'retiring'; acceptUntil: number; revokedAt?: never; } export interface IRevokedSecretRecipientMetadata extends ISecretRecipientMetadataBase { lifecycleState: 'revoked'; acceptUntil?: never; revokedAt: number; } export type TSecretRecipientMetadata = | IActiveSecretRecipientMetadata | IRetiringSecretRecipientMetadata | IRevokedSecretRecipientMetadata; export const secretRecipientLimits = Object.freeze({ maximumRecipients: 64, }); const rawX25519PublicKeyRegex = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/; export const validateSecretRecipientMetadata = ( recipientArg: unknown, ): string[] => { if (!isRecord(recipientArg)) return ['secret recipient must be an object']; const lifecycleState = recipientArg.lifecycleState; const requiredKeys = lifecycleState === 'active' ? ['schemaVersion', 'recipientKeyId', 'publicKey', 'generation', 'activatedAt', 'lifecycleState'] : lifecycleState === 'retiring' ? ['schemaVersion', 'recipientKeyId', 'publicKey', 'generation', 'activatedAt', 'lifecycleState', 'acceptUntil'] : lifecycleState === 'revoked' ? ['schemaVersion', 'recipientKeyId', 'publicKey', 'generation', 'activatedAt', 'lifecycleState', 'revokedAt'] : []; if (requiredKeys.length === 0 || !hasExactKeys(recipientArg, requiredKeys)) { return ['secret recipient must use its exact lifecycle schema']; } const errors: string[] = []; if (recipientArg.schemaVersion !== 1) errors.push('secret recipient schemaVersion must be 1'); if (!isSecretIdentifier(recipientArg.recipientKeyId)) { errors.push('secret recipient key id must be canonical'); } if (typeof recipientArg.publicKey !== 'string' || !rawX25519PublicKeyRegex.test(recipientArg.publicKey)) { errors.push('secret recipient public key must be canonical base64url raw X25519 material'); } for (const field of ['generation', 'activatedAt'] as const) { if (!Number.isSafeInteger(recipientArg[field]) || (recipientArg[field] as number) < 1) { errors.push(`secret recipient ${field} must be a positive integer`); } } if (lifecycleState === 'retiring' && (!Number.isSafeInteger(recipientArg.acceptUntil) || (recipientArg.acceptUntil as number) <= (recipientArg.activatedAt as number))) { errors.push('retiring secret recipient acceptUntil must follow activation'); } if (lifecycleState === 'revoked' && (!Number.isSafeInteger(recipientArg.revokedAt) || (recipientArg.revokedAt as number) < (recipientArg.activatedAt as number))) { errors.push('revoked secret recipient revokedAt must not precede activation'); } return errors; }; export const validateSecretRecipientSet = ( recipientsArg: unknown, ): string[] => { if (!Array.isArray(recipientsArg) || recipientsArg.length === 0 || recipientsArg.length > secretRecipientLimits.maximumRecipients) { return [ `secret recipient set must contain between 1 and ${secretRecipientLimits.maximumRecipients} entries`, ]; } const errors = recipientsArg.flatMap((recipientArg, indexArg) => ( validateSecretRecipientMetadata(recipientArg) .map((errorArg) => `recipients[${indexArg}].${errorArg}`) )); const recipients = recipientsArg.filter(isRecord); if (recipients.filter((recipientArg) => recipientArg.lifecycleState === 'active').length !== 1) { errors.push('secret recipient set must contain exactly one active recipient'); } const keyIds = recipients.map((recipientArg) => recipientArg.recipientKeyId); const generations = recipients.map((recipientArg) => recipientArg.generation); if (new Set(keyIds).size !== keyIds.length) errors.push('secret recipient key ids must be unique'); if (new Set(generations).size !== generations.length) { errors.push('secret recipient generations must be unique'); } const activeRecipient = recipients.find((recipientArg) => recipientArg.lifecycleState === 'active'); const numericGenerations = generations.filter((generationArg): generationArg is number => ( Number.isSafeInteger(generationArg) )); if (activeRecipient && Number.isSafeInteger(activeRecipient.generation) && numericGenerations.length > 0 && activeRecipient.generation !== Math.max(...numericGenerations)) { errors.push('the active secret recipient must have the greatest generation'); } return errors; }; /** New envelopes target only active recipients. */ export const canIssueNewSecretEnvelope = ( recipientArg: TSecretRecipientMetadata, ): boolean => recipientArg.lifecycleState === 'active'; export const validateSecretValueInputForRecipient = async ( valueInputArg: unknown, recipientArg: TSecretRecipientMetadata, expectedContextArg: Uint8Array, ): Promise => { const errors = validateSecretValueInput(valueInputArg); if (errors.length > 0 || !isRecord(valueInputArg) || valueInputArg.mode !== 'sealed') { return errors; } if (!canIssueNewSecretEnvelope(recipientArg)) { errors.push('new sealed inputs require the active ingress recipient'); } try { const envelope = plugins.smartcrypto.parseX25519Envelope(valueInputArg.envelope); if (envelope.recipientKeyId !== recipientArg.recipientKeyId) { errors.push('sealed input recipient does not match the active ingress recipient'); } if (!await verifySecretEnvelopeContext(envelope, expectedContextArg)) { errors.push('sealed input context does not match the request'); } } catch { // validateSecretValueInput already reports malformed envelopes. } return errors; }; const canonicalSecretOwner = (ownerArg: TSecretOwner): Record => { switch (ownerArg.kind) { case 'service': return { kind: ownerArg.kind, serviceId: ownerArg.serviceId }; case 'secret-set': return { kind: ownerArg.kind, secretSetId: ownerArg.secretSetId }; case 'platform-provider': return { kind: ownerArg.kind, providerConfigId: ownerArg.providerConfigId, }; case 'system': return { kind: ownerArg.kind, systemId: ownerArg.systemId }; } }; const canonicalSecretDelivery = (deliveryArg: TSecretDelivery): Record => ( deliveryArg.type === 'launcher-environment' ? { type: deliveryArg.type, variableName: deliveryArg.variableName, uid: deliveryArg.uid, gid: deliveryArg.gid, mode: deliveryArg.mode, } : { type: deliveryArg.type, targetPath: deliveryArg.targetPath, uid: deliveryArg.uid, gid: deliveryArg.gid, mode: deliveryArg.mode, } ); const contextBytes = (valueArg: Record): Uint8Array => ( new TextEncoder().encode(JSON.stringify(valueArg)) ); export const createSecretCreateEnvelopeContext = (contextArg: { mutationId: string; target: TSecretMutationTarget; key: string; environment: TSecretEnvironment; delivery: TSecretDelivery; expectedTargetSecretsRevision: number; }): Uint8Array => contextBytes({ schemaVersion: 1, purpose: 'serve.zone/secret-create', mutationId: contextArg.mutationId, target: canonicalSecretOwner(contextArg.target), key: contextArg.key, environment: contextArg.environment, delivery: canonicalSecretDelivery(contextArg.delivery), expectedTargetSecretsRevision: contextArg.expectedTargetSecretsRevision, }); export const createSecretRotateEnvelopeContext = (contextArg: { mutationId: string; secretId: string; expectedSecretRevision: number; expectedActiveVersionId: string; expectedTargetSecretsRevision: number; }): Uint8Array => contextBytes({ schemaVersion: 1, purpose: 'serve.zone/secret-rotate', mutationId: contextArg.mutationId, secretId: contextArg.secretId, expectedSecretRevision: contextArg.expectedSecretRevision, expectedActiveVersionId: contextArg.expectedActiveVersionId, expectedTargetSecretsRevision: contextArg.expectedTargetSecretsRevision, }); export const createAppStoreInstallSecretEnvelopeContext = (contextArg: { mutationId: string; appId: string; appVersion: string; serviceId: string; key: string; delivery: TSecretDelivery; generation?: { encoding: TSecretGeneratedEncoding; bytes: number }; }): Uint8Array => contextBytes({ schemaVersion: 1, purpose: 'serve.zone/app-install-secret', mutationId: contextArg.mutationId, appId: contextArg.appId, appVersion: contextArg.appVersion, serviceId: contextArg.serviceId, key: contextArg.key, delivery: canonicalSecretDelivery(contextArg.delivery), generation: contextArg.generation ? { encoding: contextArg.generation.encoding, bytes: contextArg.generation.bytes } : null, }); export const createResolvedSecretMaterialEnvelopeContext = (contextArg: { recipientKeyId: string; organizationId: string; clusterId: string; serviceId: string; imageRolloutId: string; imageRolloutGeneration: number; requestedImageDigest: TSha256Digest; secretRolloutId: string; secretRolloutGeneration: number; manifestId: string; manifestDigest: TSha256Digest; secretId: string; secretVersionId: string; }): Uint8Array => contextBytes({ schemaVersion: 1, purpose: 'serve.zone/resolved-secret-material', recipientKeyId: contextArg.recipientKeyId, organizationId: contextArg.organizationId, clusterId: contextArg.clusterId, serviceId: contextArg.serviceId, imageRolloutId: contextArg.imageRolloutId, imageRolloutGeneration: contextArg.imageRolloutGeneration, requestedImageDigest: contextArg.requestedImageDigest, secretRolloutId: contextArg.secretRolloutId, secretRolloutGeneration: contextArg.secretRolloutGeneration, manifestId: contextArg.manifestId, manifestDigest: contextArg.manifestDigest, secretId: contextArg.secretId, secretVersionId: contextArg.secretVersionId, }); export const createSecretRecipientEnrollmentChallengeContext = (contextArg: { enrollmentId: string; clusterId: string; recipientKeyId: string; publicKey: string; generation: number; expiresAt: number; }): Uint8Array => contextBytes({ schemaVersion: 1, purpose: 'serve.zone/secret-recipient-enrollment-challenge', enrollmentId: contextArg.enrollmentId, clusterId: contextArg.clusterId, recipientKeyId: contextArg.recipientKeyId, publicKey: contextArg.publicKey, generation: contextArg.generation, expiresAt: contextArg.expiresAt, });