/** Explicit operator process only. No HTTP, runtime import, role provisioning or READY. */ import assert from 'node:assert/strict' import path from 'node:path' import {pathToFileURL} from 'node:url' import {createPublicKey} from 'node:crypto' import {readProtected,protectedDirectory,sha} from './writer-profile/operator-files.mjs' import {approval,reconciliationApproval,poolOptions,roleKeys,assertJsDriverEnvironment} from './writer-profile/operator-contract.mjs' import {deploymentRequest} from './database-writer-bootstrap-operator.mjs' const hex=(v:unknown)=>typeof v==='string'&&/^[a-f0-9]{64}$/.test(v) const parse=(b:Buffer)=>JSON.parse(new TextDecoder('utf-8',{fatal:true}).decode(b)) export async function main(args:string[]){ if(['maintenance-inspect','maintenance-resolve'].includes(args[0]))return (await import('./writer-profile/maintenance-resolution.mjs')).maintenanceMain(args) const flags=['bootstrap','bootstrap-sha256','approval','approval-sha256','definition','definition-sha256','transport','transport-sha256','ca','source-registry','source-spki-sha256','journal'] assert(['publish','reconcile','preflight'].includes(args[0])&&args.length===1+2*flags.length) assertJsDriverEnvironment(process.env) const values=new Map() for(let i=1;ivalues.get(name)! for(const flag of flags.filter(f=>f.endsWith('sha256')))assert(hex(get(flag))) const bootstrap=parse(await readProtected(get('bootstrap'),32768,get('bootstrap-sha256'))) assert(bootstrap.format==='application-writer-bootstrap-v1'&&bootstrap.publicationAuthorized===false&&bootstrap.readyAuthorized===false) const request=deploymentRequest(bootstrap.deploymentRequest),subject=bootstrap.verificationSubject assert(subject.installationId===request.installationId&&hex(subject.publicKeySpkiSha256)&&sha(createPublicKey(subject.publicKeyPem).export({format:'der',type:'spki'}))===subject.publicKeySpkiSha256) const pinned=(args[0]==='reconcile'?reconciliationApproval:approval)(parse(await readProtected(get('approval'),16384,get('approval-sha256')))) const definition=parse(await readProtected(get('definition'),4194304,get('definition-sha256'))) const transport=parse(await readProtected(get('transport'),16384,get('transport-sha256'))) const ca=await readProtected(get('ca'),65536,request.tls.caSha256) const options=poolOptions(transport,request,ca) await protectedDirectory(get('journal'));await protectedDirectory(get('source-registry')) const privateKeyFile=path.join(path.dirname(get('bootstrap')),'signing-private.pem') if(args[0]==='publish'){const key=await readProtected(privateKeyFile,8192);key.fill(0)} // All explicit trust files are checked before app helper/PG imports. No .env URL is used. // Existing source helper loads config/dotenv. Complete it BEFORE pg's first // import so a newly loaded NODE_PG_FORCE_NATIVE cannot select another driver. const {createDatabaseSourceBindingVerifier}=await import('../src/db/database-source-binding.js') assertJsDriverEnvironment(process.env) const [{default:pg},{createConnectedPublisher},{collectSource44Catalogue,approvedDefinition}]=await Promise.all([ import('pg'),import('./writer-profile/publisher.mjs'),import('./writer-profile/catalogue.mjs')]) assertJsDriverEnvironment(process.env) assert.equal(approvedDefinition(definition),pinned.definitionSha256) const installation=Object.freeze({...subject,roles:Object.fromEntries(roleKeys.map(k=>[k,request.roles[k].signedOid])),roleBindings:request.roles,expectedSource:request.source}) const sourcePin={registryDirectory:get('source-registry'),installationId:subject.installationId,publicKeySpkiSha256:get('source-spki-sha256'),currentHeadSha256:request.sourceHeadSha256} // Preflight is the read-only lifecycle entry for protected registry evidence. // A refused protection check never falls back to the signature-only verifier. const verifier=args[0]==='preflight' ?(await import('./writer-profile/protected-source-registry.mjs')).createProtectedDatabaseSourceBindingVerifier(sourcePin) :createDatabaseSourceBindingVerifier(sourcePin) const publisher=createConnectedPublisher(installation,(s:Parameters[0])=>collectSource44Catalogue(s,installation),verifier,pinned) const pool=new pg.Pool(options) // A pool-level async error never becomes raw stdout or a retry signal. let asyncFailure=false;pool.on('error',()=>{asyncFailure=true}) let result,primary:unknown,failed=false try{result=args[0]==='preflight'?await publisher.preflightCurrentProfile(pool,{deadlineAt:Date.now()+120000}):args[0]==='reconcile'?await publisher.reconcilePendingPublication(pool,{journalDirectory:get('journal'),deadlineAt:Date.now()+120000}):await publisher.publishCurrentProfile(pool,{privateKeyFile,expectedHeadSha256:pinned.expectedHeadSha256,journalDirectory:get('journal'),validityMs:pinned.validityMs,deadlineAt:Date.now()+120000})} catch(error){primary=error;failed=true} try{await pool.end()}catch(error){primary??=error;failed=true} if(args[0]==='preflight'&&result?.state==='matched'&&result.platformProtectionVerified===true&&!failed&&!asyncFailure)return {event:'application-writer-profile-preflight',outcome:'matched',epoch:result.epoch,headSha256:result.headSha256,notAfter:result.notAfter,sourceMatched:true,rolesMatched:true,catalogueMatched:true,platformProtectionVerified:true,writerAuthorized:false,cleanupUnconfirmed:false,automaticReplay:false,generationAuthenticated:false,readyAuthorized:false} if(result?.outcome==='committed')return {event:'application-writer-profile-publication',outcome:'committed',epoch:result.epoch,headSha256:result.headSha256,notAfter:result.notAfter, cleanupUnconfirmed:result.cleanupUnconfirmed||asyncFailure||failed,journalUnconfirmed:!!result.journalUnconfirmed,automaticReplay:false,generationAuthenticated:false,readyAuthorized:false} if(args[0]==='reconcile'&&result&&!failed&&!asyncFailure&&['idle','confirmed','not-committed'].includes(result.state))return {event:'application-writer-profile-reconciliation',outcome:result.state,headSha256:result.headSha256??null,cleanupUnconfirmed:false,journalUnconfirmed:false,automaticReplay:false,generationAuthenticated:false,readyAuthorized:false} throw primary??Error('Publisher terminal unconfirmed') } if(process.argv[1]&&import.meta.url===pathToFileURL(path.resolve(process.argv[1])).href){ try{const result=await main(process.argv.slice(2));console.log(JSON.stringify(result));if(result.cleanupUnconfirmed||result.journalUnconfirmed)process.exitCode=2} catch{console.error(JSON.stringify({event:['maintenance-inspect','maintenance-resolve'].includes(process.argv[2])?'database-maintenance-command-refused':'application-writer-profile-publication-refused',automaticReplay:false,inspectPendingBeforeRetry:true,readyAuthorized:false}));process.exitCode=1} }