type VaultConfig = { address: string; token: string; mount: string; sharedPath: string; projectPath: string; }; function requireEnv(name: string) { const value = process.env[name]; if (!value) { throw new Error(`${name} is required when VAULT_ENABLED=true.`); } return value; } function vaultConfig(): VaultConfig { return { address: requireEnv('VAULT_ADDR').replace(/\/$/, ''), token: requireEnv('VAULT_TOKEN'), mount: requireEnv('VAULT_KV_MOUNT'), sharedPath: requireEnv('VAULT_SHARED_PATH'), projectPath: requireEnv('VAULT_PROJECT_PATH'), }; } async function readVaultPath(config: VaultConfig, path: string) { const response = await fetch( `${config.address}/v1/${config.mount}/data/${path}`, { headers: { 'X-Vault-Token': config.token } }, ); if (!response.ok) { throw new Error(`Vault read failed for ${path}: ${response.status}.`); } const payload = (await response.json()) as { data?: { data?: Record }; }; const data = payload.data?.data; if (!data) { throw new Error(`Vault path ${path} has no KV v2 data.`); } return data; } function applyEnvironment(values: Record) { for (const [key, value] of Object.entries(values)) { if (typeof value !== 'string') { throw new Error(`Vault value ${key} must be a string.`); } process.env[key] = value; } } export async function loadVaultEnvironment() { if (process.env.VAULT_ENABLED !== 'true') { return; } const config = vaultConfig(); applyEnvironment(await readVaultPath(config, config.sharedPath)); applyEnvironment(await readVaultPath(config, config.projectPath)); }