'use strict'; // Starts an already-built, frozen directory. There is intentionally no build, // codegen, Git checkout, publish, or fallback-to-RC operation in this entrypoint. const { createHash } = require('node:crypto'); const { readdirSync, lstatSync, readFileSync, realpathSync } = require('node:fs'); const path = require('node:path'); const { spawnSync } = require('node:child_process'); const SHA256 = /^[a-f0-9]{64}$/; const COMMIT = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/; const STABLE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/; function ensure(condition, message) { if (!condition) throw new Error(`Stable startup refused: ${message}`); } function canonicalJson(value) { if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`; if (value && typeof value === 'object') return `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(',')}}`; return JSON.stringify(value); } function candidateLockChecksum(lock) { ensure(Object.keys(lock || {}).sort().join(',') === 'branchName,buildChecksum,buildRevision,candidateVersion,moduleCode,schemaInputChecksum', 'invalid Candidate lock fields'); return createHash('sha256').update(canonicalJson(lock), 'utf8').digest('hex'); } function calculateCompiledArtifactDigest(projectDir) { const root = path.resolve(projectDir, 'dist'); ensure(lstatSync(root, { throwIfNoEntry: false })?.isDirectory(), 'dist does not exist; deploy the frozen build, do not rebuild during startup'); const files = []; function visit(directory) { for (const name of readdirSync(directory)) { const file = path.join(directory, name); const stat = lstatSync(file); ensure(!stat.isSymbolicLink(), 'compiled artifact must not contain symbolic links'); if (stat.isDirectory()) visit(file); else { ensure(stat.isFile(), 'compiled artifact contains a non-file entry'); files.push([path.relative(root, file).replace(/\\/g, '/'), file]); } } } visit(root); ensure(files.length > 0, 'compiled artifact is empty'); // Exact algorithm used by cool saas-model finalize with artifactDir: dist. const hash = createHash('sha256').update('COOL_SAAS_ARTIFACT_DIRECTORY_V1\0', 'utf8'); for (const [name, file] of files.sort(([left], [right]) => left.localeCompare(right))) { const content = readFileSync(file); hash.update(`${Buffer.byteLength(name, 'utf8')}:`, 'utf8'); hash.update(name, 'utf8'); hash.update(`:${content.length}:`, 'utf8'); hash.update(content); } return `sha256:${hash.digest('hex')}`; } function sourceCommit(projectDir, environment) { const gitOptions = { cwd: projectDir, encoding: 'utf8', windowsHide: true, timeout: 30_000 }; const root = spawnSync('git', ['rev-parse', '--show-toplevel'], gitOptions); if (root.status === 0) { ensure(path.resolve(root.stdout.trim()) === path.resolve(projectDir), 'project must be its own exact Git checkout'); const status = spawnSync('git', ['status', '--porcelain=v1', '--untracked-files=all'], gitOptions); ensure(status.status === 0 && !status.stdout.trim(), 'Git checkout is dirty'); const commit = spawnSync('git', ['rev-parse', 'HEAD'], gitOptions); ensure(commit.status === 0 && COMMIT.test(commit.stdout.trim()), 'Git HEAD is unavailable'); ensure(!environment.BUILD_SHA || environment.BUILD_SHA === commit.stdout.trim(), 'BUILD_SHA differs from Git HEAD'); return commit.stdout.trim(); } ensure(!lstatSync(path.join(projectDir, '.git'), { throwIfNoEntry: false }), 'Git metadata exists but could not be verified'); // Git-free deployment archives must carry the trusted orchestrator's commit. // This is cross-checked against both the authoritative Frozen Artifact and // its actual compiled bytes, never accepted as a digest substitute. ensure(COMMIT.test(environment.BUILD_SHA || ''), 'Git-free deployments require an exact BUILD_SHA'); return environment.BUILD_SHA; } async function fetchReleaseEvidence(environment, fetcher = fetch) { const releaseId = Number(environment.RPC_RELEASE_ID); ensure(Number.isSafeInteger(releaseId) && releaseId > 0, 'RPC_RELEASE_ID must select a Stable release'); const base = new URL(environment.SAAS_CONTROL_PLANE_URL || environment.COOL_SAAS_CONTROL_PLANE_URL || 'http://127.0.0.1:8080'); const local = ['local', 'test'].includes(environment.NODE_ENV); ensure(base.protocol === 'https:' || (local && base.protocol === 'http:' && ['127.0.0.1', 'localhost', '[::1]'].includes(base.hostname)), 'use HTTPS (loopback HTTP is local/test only)'); ensure(!base.username && !base.password, 'control-plane URL must not contain credentials'); ensure(String(environment.COOL_SAAS_TOKEN || '').trim(), 'a short-lived CLI/CI Bearer credential is required to verify immutable release evidence'); const url = new URL('/admin/saas/module_release/info', base); url.searchParams.set('id', String(releaseId)); const response = await fetcher(url, { method: 'GET', headers: { Authorization: `Bearer ${environment.COOL_SAAS_TOKEN}` }, redirect: 'error', signal: AbortSignal.timeout(10_000) }); ensure(response.ok, `control plane rejected release evidence (${response.status})`); const body = await response.json(); ensure(body?.code === 1000 && body.data, 'control plane did not return release evidence'); return body.data; } function verifyStableEvidence({ release, lock, generated, digest, commit, releaseId, mode = 'production' }) { const build = release?.build; const artifact = release?.frozenArtifact; const stable = mode === 'production'; ensure(['production', 'frozen'].includes(mode), 'unsupported artifact runtime mode'); ensure(release?.id === Number(releaseId) && release.status === 'published' && release.lifecycleVersion === 1 && (stable ? release.releaseType === 'stable' : release.releaseType === 'rc' && ['frozen', 'accepted', 'promoted'].includes(release.candidateStatus) && release.releaseEvidenceVersion === 2), 'target is not a published lifecycle Stable release or explicit V2 frozen RC'); const targetVersion = stable ? release.version : release.targetVersion; ensure(STABLE.test(targetVersion || '') && release.serviceVersion === release.version, 'Stable version identity is invalid'); ensure(build && artifact && Number(stable ? release.promotedFromReleaseId : release.id) === Number(artifact.rcReleaseId) && Number(release.frozenArtifactId) === Number(artifact.id), 'Stable does not reference its source RC frozen artifact'); ensure(Number(build.id) === Number(release.buildSnapshotId) && Number(build.moduleId) === Number(release.moduleId) && Number(artifact.moduleId) === Number(release.moduleId), 'Build/module/artifact association differs'); ensure(Number(build.revision) === Number(lock.buildRevision) && Number(release.buildRevision) === Number(lock.buildRevision) && Number(artifact.buildRevision) === Number(lock.buildRevision), 'Build Revision differs from the Candidate lock'); ensure(SHA256.test(build.buildChecksum || '') && build.buildChecksum === lock.buildChecksum && build.schemaInputChecksum === lock.schemaInputChecksum, 'Build checksums differ from the Candidate lock'); ensure(lock.candidateVersion === `${targetVersion}-rc.${lock.buildRevision}` && lock.branchName === `release/${lock.candidateVersion}` && artifact.sourceBranch === lock.branchName && artifact.tagName === `v${lock.candidateVersion}` && (stable || release.version === lock.candidateVersion), 'source Candidate branch/version/tag differs'); ensure(candidateLockChecksum(lock) === artifact.generatedLockChecksum, 'generated Candidate lock checksum differs'); ensure(COMMIT.test(artifact.sourceCommit || '') && artifact.sourceCommit === commit, 'source commit differs from the Frozen Artifact'); ensure(/^sha256:[a-f0-9]{64}$/.test(artifact.artifactDigest || ''), 'Frozen Artifact has no compiled digest; old Git-only evidence cannot be upgraded or forged here'); ensure(artifact.artifactDigest === digest, 'compiled artifact digest differs from the Frozen Artifact'); ensure(SHA256.test(artifact.artifactIdentityChecksum || '') && SHA256.test(artifact.implementationManifestChecksum || ''), 'frozen implementation identity is incomplete'); ensure(generated.moduleCode === lock.moduleCode && generated.revision === lock.buildRevision && generated.schemaInputChecksum === lock.schemaInputChecksum, 'compiled generated identity differs from the immutable Build'); ensure(!generated.releaseVersion || generated.releaseVersion === targetVersion, 'generated runtime embeds a different Release version'); ensure(!generated.implementationManifestChecksum || generated.implementationManifestChecksum === artifact.implementationManifestChecksum, 'compiled implementation checksum differs'); const manifest = release.schemaSnapshot?.actionManifest; const contracts = release.schemaSnapshot?.runtimeContracts; // A frozen RC keeps its original immutable envelope. Its actual bytes are // selected by the separately signed Frozen Artifact; Stable's new envelope // must explicitly reuse that digest. ensure(manifest && contracts?.implementationManifestChecksum === artifact.implementationManifestChecksum && (stable ? manifest.artifactDigest === artifact.artifactDigest : /^sha256:[a-f0-9]{64}$/.test(manifest.artifactDigest || '')), 'Stable release envelope differs from the frozen implementation/artifact'); ensure(generated.runtimeContractChecksum === contracts.runtimeContractChecksum && SHA256.test(contracts.runtimeContractChecksum || ''), 'compiled Runtime contract checksum differs'); ensure(generated.schemaVersion === release.schemaVersion && build.schemaVersion === release.schemaVersion, 'compiled Schema version differs'); return { SAAS_RUNTIME_MODE: mode, RPC_REQUIRE_EXACT_RELEASE: 'true', RPC_MODULE_CODE: lock.moduleCode, RPC_RELEASE_ID: String(release.id), RPC_RELEASE_VERSION: release.version, RPC_SERVICE_VERSION: release.serviceVersion, SAAS_BUILD_REVISION: String(build.revision), SAAS_BUILD_CHECKSUM: build.buildChecksum, SAAS_SCHEMA_INPUT_CHECKSUM: build.schemaInputChecksum, RPC_MIN_SCHEMA_VERSION: String(release.schemaVersion), RPC_MAX_SCHEMA_VERSION: String(release.schemaVersion), RPC_SCHEMA_CHECKSUM: release.schemaChecksum, RPC_ACTION_MANIFEST_CHECKSUM: release.schemaSnapshot.actionManifestChecksum, RPC_RUNTIME_CONTRACT_CHECKSUM: contracts.runtimeContractChecksum, RPC_IMPLEMENTATION_MANIFEST_CHECKSUM: artifact.implementationManifestChecksum, RPC_ARTIFACT_DIGEST: artifact.artifactDigest, RPC_ARTIFACT_IDENTITY_CHECKSUM: artifact.artifactIdentityChecksum, BUILD_SHA: artifact.sourceCommit }; } function applyExactEnvironment(environment, exact) { for (const [key, value] of Object.entries(exact)) { ensure(value != null && String(value).length > 0, `authoritative field ${key} is missing`); ensure(!environment[key] || environment[key] === String(value), `${key} conflicts with authoritative Stable evidence`); environment[key] = String(value); } } function verifyBootstrapSession(session, exact) { ensure(session?.moduleCode === exact.RPC_MODULE_CODE && session.revision === Number(exact.SAAS_BUILD_REVISION), 'bootstrap module/Revision differs'); const mapping = { releaseId: 'RPC_RELEASE_ID', releaseVersion: 'RPC_RELEASE_VERSION', serviceVersion: 'RPC_SERVICE_VERSION', runtimeMode: 'SAAS_RUNTIME_MODE', buildRevision: 'SAAS_BUILD_REVISION', buildChecksum: 'SAAS_BUILD_CHECKSUM', schemaInputChecksum: 'SAAS_SCHEMA_INPUT_CHECKSUM', implementationManifestChecksum: 'RPC_IMPLEMENTATION_MANIFEST_CHECKSUM', artifactDigest: 'RPC_ARTIFACT_DIGEST', artifactIdentityChecksum: 'RPC_ARTIFACT_IDENTITY_CHECKSUM', schemaChecksum: 'RPC_SCHEMA_CHECKSUM', actionManifestChecksum: 'RPC_ACTION_MANIFEST_CHECKSUM', runtimeContractChecksum: 'RPC_RUNTIME_CONTRACT_CHECKSUM' }; for (const [field, key] of Object.entries(mapping)) ensure(String(session.release?.[field]) === exact[key], `authenticated bootstrap ${field} differs from Stable evidence`); } async function verifyTrustedReleaseBundle(projectDir, environment, release, fetcher = fetch) { const file = String(environment.SAAS_RELEASE_TRUST_FILE || ''); ensure(path.isAbsolute(file) && lstatSync(file, {throwIfNoEntry:false})?.isFile() && !lstatSync(file).isSymbolicLink(), 'V2 startup needs an external build trust configuration'); const relative = path.relative(realpathSync(projectDir), realpathSync(file)); ensure(path.isAbsolute(relative) || relative === '..' || relative.startsWith('..' + path.sep), 'build trust must be supplied outside the signed deployment'); const trust = JSON.parse(readFileSync(file, 'utf8')); const artifact = release.frozenArtifact; const keyId = artifact?.buildEvidence?.keyId; const builder = trust.builders?.[keyId]; ensure(builder && builder.enabled !== false && builder.moduleIds?.includes(release.moduleId) && builder.repositories?.includes(artifact.sourceRepository), 'build signer is not trusted for this module and repository'); const helper = require('./release-artifact'); const claims = helper.verifyReleaseEvidence('build', artifact.buildEvidence, {[keyId]:builder.publicKey}); ensure(release.releaseEvidenceVersion === 2 && artifact.releaseEvidenceVersion === 2 && claims.version === 2, 'release evidence version differs'); for (const [field, expected] of Object.entries({ releaseId: artifact.rcReleaseId, moduleId: release.moduleId, buildRevision: release.buildRevision, buildChecksum: release.build.buildChecksum, sourceRepository: artifact.sourceRepository, sourceBranch: artifact.sourceBranch, sourceCommit: artifact.sourceCommit, tagName: artifact.tagName, generatedLockChecksum: artifact.generatedLockChecksum, implementationManifestChecksum: artifact.implementationManifestChecksum, dependencyLockChecksum: artifact.dependencyLockChecksum, bundleDigest: artifact.bundleDigest, artifactDigest: artifact.artifactDigest, })) ensure(claims[field] === expected, `build signature ${field} differs from the frozen artifact`); const base = new URL(environment.SAAS_CONTROL_PLANE_URL || environment.COOL_SAAS_CONTROL_PLANE_URL || 'http://127.0.0.1:8080'); const url = new URL('/admin/saas/module_release/artifact-bundle',base); url.searchParams.set('releaseId',String(release.id)); const response = await fetcher(url,{headers:{Authorization:`Bearer ${environment.COOL_SAAS_TOKEN}`},redirect:'error',signal:AbortSignal.timeout(30000)}); ensure(response.ok, `trusted artifact is unavailable or revoked (${response.status})`); const body = await response.json(); ensure(body?.code === 1000 && body.data, 'trusted artifact response is invalid'); const evidence = body.data; ensure(evidence.bundleDigest === artifact.bundleDigest && evidence.artifactDigest === artifact.artifactDigest && helper.releaseJsonChecksum(evidence.buildEvidence) === helper.releaseJsonChecksum(artifact.buildEvidence), 'stored artifact and signed source differ'); const inspected = helper.verifyLocalReleaseArtifact(projectDir,evidence.bundle); ensure(inspected.artifactDigest === artifact.artifactDigest && inspected.bundleDigest === artifact.bundleDigest, 'deployed bytes differ from the trusted artifact'); ensure(createHash('sha256').update(inspected.files.get('pnpm-lock.yaml')).digest('hex') === artifact.dependencyLockChecksum, 'signed dependency lock differs'); return { recheck: () => helper.verifyLocalReleaseArtifact(projectDir,evidence.bundle) }; } async function startStable(options = {}) { const projectDir = path.resolve(options.projectDir || __dirname); const environment = options.environment || process.env; const release = await fetchReleaseEvidence(environment, options.fetch); const mode = options.mode || 'production'; const lock = JSON.parse(readFileSync(path.join(projectDir, '.cool-saas-candidate.json'), 'utf8')); const digest = calculateCompiledArtifactDigest(projectDir); const commit = sourceCommit(projectDir, environment); // Do not execute generated JavaScript before its compiled digest is verified. ensure(digest === release.frozenArtifact?.artifactDigest, 'compiled artifact digest differs from the Frozen Artifact'); ensure(release.releaseEvidenceVersion == null || [1, 2].includes(release.releaseEvidenceVersion), 'unknown release evidence version'); const trusted = release.releaseEvidenceVersion === 2 ? await verifyTrustedReleaseBundle(projectDir,environment,release,options.fetch) : null; const generated = (options.loadGenerated || (() => require(path.join(projectDir, 'dist/saas/generated')).GeneratedSaasModule))(); const exact = verifyStableEvidence({ release, lock, generated, digest, commit, releaseId: environment.RPC_RELEASE_ID, mode }); applyExactEnvironment(environment, exact); environment.SAAS_CONTROL_PLANE_URL ||= environment.COOL_SAAS_CONTROL_PLANE_URL || 'http://127.0.0.1:8080'; delete environment.COOL_SAAS_TOKEN; delete environment.NODE_AUTH_TOKEN; delete environment.npm_config__authToken; const start = options.start || require(path.join(projectDir, 'bootstrap.js')).start; return start({ verifySession: session => { verifyBootstrapSession(session, exact); ensure(calculateCompiledArtifactDigest(projectDir) === digest, 'compiled bytes changed during bootstrap'); trusted?.recheck(); } }); } if (require.main === module) startStable().catch(error => { process.stderr.write(`${error?.message || error}\n`); process.exitCode = 1; }); module.exports = { startStable, verifyStableEvidence, verifyBootstrapSession, applyExactEnvironment, calculateCompiledArtifactDigest, candidateLockChecksum, fetchReleaseEvidence, sourceCommit, verifyTrustedReleaseBundle };