Add ensureLocalSecretsKeyFile helper that generates a random 32-byte master key during onboard if using local_encrypted provider. Move resolveRuntimeLikePath to cli/src/utils/ for reuse by secrets-key and existing check modules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
import { randomBytes } from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import type { PaperclipConfig } from "./schema.js";
|
|
import { resolveRuntimeLikePath } from "../utils/path-resolver.js";
|
|
|
|
export type EnsureSecretsKeyResult =
|
|
| { status: "created"; path: string }
|
|
| { status: "existing"; path: string }
|
|
| { status: "skipped_env"; path: null }
|
|
| { status: "skipped_provider"; path: null };
|
|
|
|
export function ensureLocalSecretsKeyFile(
|
|
config: Pick<PaperclipConfig, "secrets">,
|
|
configPath?: string,
|
|
): EnsureSecretsKeyResult {
|
|
if (config.secrets.provider !== "local_encrypted") {
|
|
return { status: "skipped_provider", path: null };
|
|
}
|
|
|
|
const envMasterKey = process.env.PAPERCLIP_SECRETS_MASTER_KEY;
|
|
if (envMasterKey && envMasterKey.trim().length > 0) {
|
|
return { status: "skipped_env", path: null };
|
|
}
|
|
|
|
const keyFileOverride = process.env.PAPERCLIP_SECRETS_MASTER_KEY_FILE;
|
|
const configuredPath =
|
|
keyFileOverride && keyFileOverride.trim().length > 0
|
|
? keyFileOverride.trim()
|
|
: config.secrets.localEncrypted.keyFilePath;
|
|
const keyFilePath = resolveRuntimeLikePath(configuredPath, configPath);
|
|
|
|
if (fs.existsSync(keyFilePath)) {
|
|
return { status: "existing", path: keyFilePath };
|
|
}
|
|
|
|
fs.mkdirSync(path.dirname(keyFilePath), { recursive: true });
|
|
fs.writeFileSync(keyFilePath, randomBytes(32).toString("base64"), {
|
|
encoding: "utf8",
|
|
mode: 0o600,
|
|
});
|
|
try {
|
|
fs.chmodSync(keyFilePath, 0o600);
|
|
} catch {
|
|
// best effort
|
|
}
|
|
return { status: "created", path: keyFilePath };
|
|
}
|