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).
50 lines
1.9 KiB
TypeScript
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);
|
|
});
|