Add keep overview command and a docker-compose deploy wrapper
All checks were successful
Docker / build-and-push (push) Successful in 1m57s

keep overview gives an admin a cross-vault view of every recipient and
grant in one screen, instead of walking vaults one at a time via
/vaults/:key/recipients. scripts/deploy.sh pulls a project's env from
keep before running docker compose up, for VPS deploys of several
docker-compose projects.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-07-12 20:16:29 +02:00
parent 461c972f92
commit f6a1ac13f2
7 changed files with 106 additions and 2 deletions

View File

@@ -18,3 +18,22 @@ export async function recipientRemove(recipientId: string): Promise<void> {
await expectOk(await adminFetch('DELETE', `/api/admin/recipients/${encodeURIComponent(recipientId)}`));
console.log(`recipient removed: ${recipientId} (and all their vault grants)`);
}
export async function adminOverview(): Promise<void> {
const data = await expectOk(await adminFetch('GET', '/api/admin/overview'));
console.log('recipients:');
if (!data.recipients.length) console.log(' none yet.');
for (const r of data.recipients) {
console.log(` ${r.recipientId} ${r.label}`);
}
console.log('\nvaults:');
if (!data.vaults.length) console.log(' none yet.');
for (const v of data.vaults) {
console.log(` ${v.vaultKey}`);
for (const g of v.grants) {
console.log(` ${g.canWrite ? 'rw' : 'r-'} ${g.recipientId} ${g.label ?? '(unknown recipient)'}`);
}
}
}

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
import { identityInit, identityShow, identitySetId } from './commands/identity.js';
import { recipientAdd, recipientList, recipientRemove } from './commands/recipient.js';
import { recipientAdd, recipientList, recipientRemove, adminOverview } from './commands/recipient.js';
import { vaultPush, vaultPull, vaultGrant, vaultRevoke, vaultLog } from './commands/vault.js';
function flag(args: string[], name: string, fallback?: string): string | undefined {
@@ -28,6 +28,8 @@ async function main(): Promise<void> {
if (sub === 'remove') return await recipientRemove(rest[0]);
}
if (cmd === 'overview') return await adminOverview();
if (cmd === 'push') return await vaultPush(sub, flag(rest, 'file', '.env')!, boolFlag(rest, 'purge'));
if (cmd === 'pull') return await vaultPull(sub, (flag(rest, 'format', 'env') as 'env' | 'json'), flag(rest, 'out'), boolFlag(rest, 'previous'));
if (cmd === 'grant') return await vaultGrant(sub, rest[0], boolFlag(rest, 'read-only'));
@@ -58,6 +60,7 @@ function printUsage(): void {
keep recipient add --label <label> --pubkey <hex> (admin)
keep recipient list (admin)
keep recipient remove <recipient-id> (admin)
keep overview (admin — every vault, every grant, one screen)
Env: KEEP_SERVER_URL (default http://localhost:3050), KEEP_ADMIN_PASSWORD (for admin commands)`);
}