godcrm/backend/scripts/seed-bluesky-secrets.mjs
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

83 lines
2.6 KiB
JavaScript

#!/usr/bin/env node
/**
* One-shot: seed Bluesky usher (handle + app-password) into `_secrets` vault.
* ADR-0040 P3. Idempotent — refuses to clobber existing rows (rotation goes
* through the Settings UI). Reads creds from argv to keep them out of git.
*
* node backend/scripts/seed-bluesky-secrets.mjs <handle> <app-password>
*
* Exit: 0 ok / 2 vault not configured / 1 write failed.
*/
import dotenv from 'dotenv';
dotenv.config();
import vault from '../services/secrets/SecretsVault.js';
import { getAdapter as getDbAdapter } from '../database/connection.js';
const [handle, appPassword] = process.argv.slice(2);
const ACTOR = 1; // space-11 owner
const ENTRIES = [
{
key: 'bluesky_handle',
value: handle,
description: '[social] Bluesky handle (AT Protocol amplifier graph — Austin recon usher)',
},
{
key: 'bluesky_app_password',
value: appPassword,
description: '[social] Bluesky app-password (revocable; AT Protocol auth — Austin recon usher)',
},
];
async function main() {
if (!handle || !appPassword) {
console.error('usage: seed-bluesky-secrets.mjs <handle> <app-password>');
process.exit(1);
}
if (!process.env.SECRETS_MASTER_KEY) {
console.error('❌ SECRETS_MASTER_KEY not set — refusing to seed.');
process.exit(2);
}
const adapter = await getDbAdapter();
await vault.init({ adapter, allowEnvFallback: false });
let failed = 0;
for (const e of ENTRIES) {
const existing = await adapter.query(
`SELECT id FROM _secrets WHERE key = $1 LIMIT 1`,
[e.key]
);
if (existing.rowCount > 0) {
console.log(`[SKIP] ${e.key.padEnd(24)} — already in vault (rotate via Settings UI)`);
continue;
}
try {
await vault.putSecret(e.key, e.value, { actor: ACTOR, description: e.description });
console.log(`[SEED] ${e.key.padEnd(24)} ← (${e.value.length} chars)`);
} catch (err) {
console.log(`[FAIL] ${e.key.padEnd(24)}${err.message}`);
failed++;
}
}
// Read-back proof (decrypt round-trip) without printing the secret.
for (const e of ENTRIES) {
const got = await vault.getSecret(e.key);
const ok = got != null;
console.log(`[VERIFY] ${e.key.padEnd(24)}${ok ? 'decrypt OK (' + got.length + ' chars)' : 'MISSING'}`);
if (!ok) failed++;
}
await vault.shutdown();
if (adapter && typeof adapter.close === 'function') {
try { await adapter.close(); } catch { /* ignore */ }
}
process.exit(failed > 0 ? 1 : 0);
}
main().catch((err) => {
console.error('seed-bluesky-secrets: fatal:', err);
process.exit(1);
});