import { randomBytes, generateKeyPairSync } from 'node:crypto' import { execFileSync } from 'node:child_process' import { chmod, mkdir, open, readFile, rename } from 'node:fs/promises' import { join } from 'node:path' // Operator-only runtime material. Never source this directory in image builds. const dir = '.dev-state/production' await mkdir(dir, { recursive: true, mode: 0o700 }) await chmod(dir, 0o700) const file = join(dir, 'secrets.json') async function persist(value) { const temporary = `${file}.${randomBytes(12).toString('hex')}.tmp` const handle = await open(temporary, 'wx', 0o600) try { await handle.writeFile(JSON.stringify(value)); await handle.sync() } finally { await handle.close() } await rename(temporary, file) await chmod(file, 0o600) } let secrets try { secrets = JSON.parse(await readFile(file, 'utf8')) } catch (error) { if (error.code !== 'ENOENT') throw error secrets = { signingKey: generateKeyPairSync('ec', { namedCurve: 'P-256' }).privateKey.export({ type: 'pkcs8', format: 'pem' }), sessionSecret: randomBytes(48).toString('base64'), tokenEncryptionKey: randomBytes(32).toString('base64') } await persist(secrets) } if (!secrets.happyviewClientKey) { secrets.happyviewClientKey = `hvc_${randomBytes(24).toString('hex')}` await persist(secrets) } await chmod(file, 0o600) if (process.argv.includes('--print-public-client-key')) { console.log(secrets.happyviewClientKey); process.exit(0) } const fly = process.env.FLYCTL_BIN ?? 'flyctl' execFileSync(fly, ['secrets', 'set', '--stage', '--app', 'reams-ink-auth', `OAUTH_KEY_FILE=${Buffer.from(secrets.signingKey).toString('base64')}`], { stdio: 'pipe' }) execFileSync(fly, ['secrets', 'set', '--stage', '--app', 'reams-ink-spaces', `SESSION_SECRET=${secrets.sessionSecret}`, `TOKEN_ENCRYPTION_KEY=${secrets.tokenEncryptionKey}`], { stdio: 'pipe' }) console.log('Runtime auth/HappyView secrets staged; persistent local recovery copy is private and excluded from builds/Git.')