'use strict'; const path = require('path'); const { statSync } = require('fs'); function requireRuntimeFile(environment, field, minimumSize, maximumSize) { const file = String(environment[field] || '').trim(); if (!path.isAbsolute(file)) { throw new Error(`${field} must be absolute`); } const stat = statSync(file, { throwIfNoEntry: false }); if (!stat?.isFile() || stat.size < minimumSize || stat.size > maximumSize) { throw new Error(`${field} is unavailable or invalid`); } } function requireExactReleaseEnvironment(environment = process.env) { const runtimeEnvironment = String(environment.NODE_ENV || '') .trim() .toLowerCase(); const localOrTest = ['local', 'test'].includes(runtimeEnvironment); const required = runtimeEnvironment === 'prod' || runtimeEnvironment === 'production' || environment.RPC_REQUIRE_EXACT_RELEASE === 'true'; if (!required) return; const fields = [ 'RPC_SERVICE_VERSION', 'RPC_MIN_SCHEMA_VERSION', 'RPC_MAX_SCHEMA_VERSION', 'RPC_RELEASE_ID', 'RPC_RELEASE_VERSION', 'RPC_SCHEMA_CHECKSUM', 'RPC_ACTION_MANIFEST_CHECKSUM', 'RPC_RUNTIME_CONTRACT_CHECKSUM', 'RPC_IMPLEMENTATION_MANIFEST_CHECKSUM', 'RPC_ARTIFACT_DIGEST', 'BUILD_SHA', 'SAAS_CONTROL_PLANE_URL', 'SAAS_TENANT_CREDENTIALS_FILE', ]; const missing = fields.filter( field => !String(environment[field] || '').trim() ); if (missing.length) { throw new Error( `Exact SaaS Release environment is incomplete: ${missing.join(', ')}` ); } const bootstrapTicketFile = String( environment.SAAS_RUNTIME_BOOTSTRAP_TOKEN_FILE || '' ).trim(); const bootstrapStateFile = String( environment.SAAS_RUNTIME_BOOTSTRAP_STATE_FILE || '' ).trim(); const configuredIdentityReady = [ 'RPC_INSTANCE_ID', 'SAAS_RUNTIME_NODE_ID', 'SAAS_SERVICE_TOKEN_FILE', ].every(field => String(environment[field] || '').trim()); if ( String(environment.SAAS_RUNTIME_BOOTSTRAP_TOKEN || '').trim() && ['prod', 'production'].includes(runtimeEnvironment) ) { throw new Error( 'Production SaaS runtime bootstrap tokens must be mounted as a file' ); } if ( !bootstrapTicketFile && !bootstrapStateFile && !(localOrTest && configuredIdentityReady) ) { throw new Error( 'Strict SaaS runtime identity requires an authenticated orchestrator bootstrap ticket/state; configured RPC_INSTANCE_ID and SAAS_RUNTIME_NODE_ID are local/test migration compatibility only' ); } if ( environment.RPC_MODULE_CODE && !/^[a-z][a-z0-9_]{0,62}$/.test(environment.RPC_MODULE_CODE) ) { throw new Error('RPC_MODULE_CODE is invalid'); } if ( environment.RPC_INSTANCE_ID && !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test( environment.RPC_INSTANCE_ID ) ) { throw new Error('RPC_INSTANCE_ID must be a UUID'); } const releaseId = Number(environment.RPC_RELEASE_ID); const minSchema = Number(environment.RPC_MIN_SCHEMA_VERSION); const maxSchema = Number(environment.RPC_MAX_SCHEMA_VERSION); const runtimeNodeId = environment.SAAS_RUNTIME_NODE_ID ? Number(environment.SAAS_RUNTIME_NODE_ID) : undefined; if ( !Number.isSafeInteger(releaseId) || releaseId < 1 || !Number.isSafeInteger(minSchema) || minSchema < 1 || minSchema !== maxSchema || (runtimeNodeId !== undefined && (!Number.isSafeInteger(runtimeNodeId) || runtimeNodeId < 0 || runtimeNodeId > 65_535)) ) { throw new Error('Exact SaaS Release numeric identity is invalid'); } for (const field of [ 'RPC_SCHEMA_CHECKSUM', 'RPC_ACTION_MANIFEST_CHECKSUM', 'RPC_RUNTIME_CONTRACT_CHECKSUM', 'RPC_IMPLEMENTATION_MANIFEST_CHECKSUM', ]) { if (!/^[a-f0-9]{64}$/.test(environment[field])) { throw new Error(`${field} must be a SHA-256 checksum`); } } if (!/^sha256:[a-f0-9]{64}$/.test(environment.RPC_ARTIFACT_DIGEST)) { throw new Error('RPC_ARTIFACT_DIGEST must be a sha256 digest'); } if (!/^[0-9A-Za-z._-]{6,128}$/.test(environment.BUILD_SHA)) { throw new Error('BUILD_SHA is invalid'); } if ( !/^\d+\.\d+\.\d+(?:-rc\.\d+)?(?:\+[0-9A-Za-z.-]+)?$/.test( environment.RPC_SERVICE_VERSION ) ) { throw new Error('RPC_SERVICE_VERSION is invalid'); } if ( !/^[0-9A-Za-z][0-9A-Za-z.+_-]{0,95}$/.test( environment.RPC_RELEASE_VERSION ) ) { throw new Error('RPC_RELEASE_VERSION is invalid'); } let controlPlane; try { controlPlane = new URL(environment.SAAS_CONTROL_PLANE_URL); } catch { throw new Error('SAAS_CONTROL_PLANE_URL is invalid'); } if (!['http:', 'https:'].includes(controlPlane.protocol)) { throw new Error('SAAS_CONTROL_PLANE_URL is invalid'); } if ( ['prod', 'production'].includes(runtimeEnvironment) && controlPlane.protocol !== 'https:' ) { throw new Error('Production SAAS_CONTROL_PLANE_URL must use HTTPS'); } if (bootstrapTicketFile) { requireRuntimeFile( environment, 'SAAS_RUNTIME_BOOTSTRAP_TOKEN_FILE', 32, 16_384 ); } if (bootstrapStateFile) { requireRuntimeFile( environment, 'SAAS_RUNTIME_BOOTSTRAP_STATE_FILE', 2, 1024 * 1024 ); } if (environment.SAAS_SERVICE_TOKEN_FILE) { requireRuntimeFile( environment, 'SAAS_SERVICE_TOKEN_FILE', 32, 16_384 ); } requireRuntimeFile( environment, 'SAAS_TENANT_CREDENTIALS_FILE', 2, 1024 * 1024 ); } module.exports = { requireExactReleaseEnvironment };