import crypto from 'crypto'; function randomHex(bytes = 32): string { return crypto.randomBytes(bytes).toString('hex'); } function randomPassword(length = 16): string { // Generate password meeting CML policy: 12+ chars, uppercase, lowercase, digit const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const lower = 'abcdefghijklmnopqrstuvwxyz'; const digits = '0123456789'; // Avoid $, #, !, % — they break Docker Compose .env files // ($=var expansion, #=comment, !=bash history, %=printf) const special = '@&*_-+='; const all = upper + lower + digits + special; // Ensure at least one of each required class const required = [ upper[crypto.randomInt(upper.length)], lower[crypto.randomInt(lower.length)], digits[crypto.randomInt(digits.length)], special[crypto.randomInt(special.length)], ]; // Fill remaining with random chars const remaining = Array.from({ length: length - required.length }, () => all[crypto.randomInt(all.length)] ); // Shuffle const chars = [...required, ...remaining]; for (let i = chars.length - 1; i > 0; i--) { const j = crypto.randomInt(i + 1); [chars[i], chars[j]] = [chars[j], chars[i]]; } return chars.join(''); } export interface InstanceSecrets { postgresPassword: string; redisPassword: string; jwtAccessSecret: string; jwtRefreshSecret: string; encryptionKey: string; initialAdminPassword: string; nocodbAdminPassword: string; grafanaAdminPassword: string; listmonkAdminPassword: string; listmonkApiToken: string; giteaAdminPassword: string; n8nEncryptionKey: string; gancioAdminPassword: string; } export function generateSecrets(adminEmail: string): InstanceSecrets & { adminEmail: string } { return { adminEmail, postgresPassword: randomHex(16), redisPassword: randomHex(16), jwtAccessSecret: randomHex(32), jwtRefreshSecret: randomHex(32), encryptionKey: randomHex(32), initialAdminPassword: randomPassword(16), nocodbAdminPassword: randomPassword(16), grafanaAdminPassword: randomPassword(16), listmonkAdminPassword: randomPassword(16), listmonkApiToken: randomHex(16), giteaAdminPassword: randomPassword(16), n8nEncryptionKey: randomHex(32), gancioAdminPassword: randomPassword(16), }; }