import fs from 'node:fs'; import path from 'node:path'; import os from 'node:os'; import { Identity } from '../shared/crypto.js'; const DIR = path.join(os.homedir(), '.keep'); const FILE = path.join(DIR, 'identity.json'); interface StoredIdentity { seedHex: string; // Assigned by whoever runs `keep recipient add` after registering this // identity's public key — not derivable from the keypair itself, so it // has to be stored once known via `keep identity set-id`. recipientId?: string; } function readStored(): StoredIdentity | null { if (!fs.existsSync(FILE)) return null; return JSON.parse(fs.readFileSync(FILE, 'utf8')) as StoredIdentity; } function writeStored(data: StoredIdentity): void { fs.mkdirSync(DIR, { recursive: true, mode: 0o700 }); fs.writeFileSync(FILE, JSON.stringify(data, null, 2), { mode: 0o600 }); } export async function loadOrCreateIdentity(): Promise<{ identity: Identity; recipientId: string | null; created: boolean }> { const stored = readStored(); if (stored) { return { identity: await Identity.fromSeedHex(stored.seedHex), recipientId: stored.recipientId ?? null, created: false }; } const identity = await Identity.generate(); writeStored({ seedHex: identity.seedHex }); return { identity, recipientId: null, created: true }; } export function setRecipientId(recipientId: string): void { const stored = readStored(); if (!stored) throw new Error("no local identity — run 'keep identity init' first"); writeStored({ ...stored, recipientId }); } export async function requireIdentityAndRecipientId(): Promise<{ identity: Identity; recipientId: string }> { const stored = readStored(); if (!stored) throw new Error("no local identity — run 'keep identity init' first"); if (!stored.recipientId) { throw new Error( "this identity has no recipient id set yet — after an admin registers your public key " + "('keep identity show' to get it), run 'keep identity set-id ' with the id they give you back", ); } return { identity: await Identity.fromSeedHex(stored.seedHex), recipientId: stored.recipientId }; }