maestro/scripts/vapid-rotate.ts
clade 7049a874f3 feat: initial public release (MAESTRO v0.1.0)
Open-source release of MAESTRO, an agent orchestration platform that runs
LLM-driven tasks through sandboxed tools, with a web UI. Apache-2.0.
See README.md and docs/ (getting-started, configuration, architecture).
2026-06-03 04:01:14 +00:00

50 lines
1.9 KiB
TypeScript

#!/usr/bin/env tsx
/**
* Rotate the VAPID keypair used by Web Push notifications.
*
* Behavior:
* 1. Read the current key from `data/secrets/vapid.json` (or wherever
* `notifications.push.vapid_current_path` points, default
* `data/secrets/vapid.json`).
* 2. Move it to the history directory (default `data/secrets/vapid-history/`).
* 3. Generate a fresh keypair and write it as the new current.
* 4. Existing subscriptions in the DB are NOT modified — they retain the
* old `vapid_key_id` and will still receive pushes (we read from history
* based on that id). To force re-subscribe, delete the history file
* manually after a grace period; users will see 401 on next push and
* can re-subscribe from Settings.
*
* Usage:
* npm run vapid-rotate
*/
import { join } from 'path';
import { loadConfig } from '../src/config.js';
import { VapidKeyStore } from '../src/vapid-store.js';
async function main(): Promise<void> {
const config = await loadConfig();
const pushCfg = config.notifications?.push;
const subject =
pushCfg?.vapidSubject ?? 'https://maestro.example.com/';
const currentPath = join(process.cwd(), 'data/secrets/vapid.json');
const historyDir =
pushCfg?.vapidHistoryDir ?? join(process.cwd(), 'data/secrets/vapid-history');
const store = new VapidKeyStore(currentPath, historyDir);
const newKey = store.rotate(subject);
console.log('VAPID rotation complete.');
console.log(` new keyId: ${newKey.keyId}`);
console.log(` public key: ${newKey.publicKey}`);
console.log(` history dir: ${historyDir}`);
console.log('');
console.log('Existing subscriptions remain valid via the history key.');
console.log('To force re-subscribe, remove the history file after the grace period');
console.log('and notify users via Settings → Notifications.');
}
main().catch(err => {
console.error('VAPID rotation failed:', err);
process.exit(1);
});