'use client' import { useEffect, useState } from 'react' import { Lock, Plus, Eye, EyeOff, Trash2, RefreshCw, Shield, AlertCircle } from 'lucide-react' interface Secret { name: string; size: number; modifiedAt: string; isDir: boolean } function timeAgo(iso?: string) { if (!iso) return '—' try { const diff = Date.now() - new Date(iso).getTime() const m = Math.floor(diff / 60000), h = Math.floor(m / 60), d = Math.floor(h / 24) if (d > 0) return `${d}d ago`; if (h > 0) return `${h}h ago`; if (m > 0) return `${m}m ago`; return 'just now' } catch { return '—' } } export default function VaultPage() { const [secrets, setSecrets] = useState([]) const [vaultExists, setVaultExists] = useState(false) const [loading, setLoading] = useState(true) const [adding, setAdding] = useState(false) const [form, setForm] = useState({ name: '', value: '', category: 'general' }) const [saving, setSaving] = useState(false) const [visible, setVisible] = useState>({}) const [output, setOutput] = useState('') const load = () => { setLoading(true) fetch('/api/vault?action=list').then(r => r.json()).then(d => { setSecrets(d.secrets || []) setVaultExists(d.vaultExists) setLoading(false) }).catch(() => setLoading(false)) } useEffect(() => { load() }, []) const addSecret = async () => { if (!form.name || !form.value) return setSaving(true) const r = await fetch('/api/vault', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'add', ...form }) }) const d = await r.json() setOutput(d.output || '') setSaving(false) if (d.success !== false) { setAdding(false); setForm({ name: '', value: '', category: 'general' }); load() } } const viewSecret = async (name: string) => { if (visible[name] !== undefined) { setVisible(v => { const n = { ...v }; delete n[name]; return n }); return } const r = await fetch('/api/vault', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'get', name }) }) const d = await r.json() setVisible(v => ({ ...v, [name]: d.output || '' })) } const deleteSecret = async (name: string) => { if (!confirm(`Delete "${name}"?`)) return const r = await fetch('/api/vault', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'delete', name }) }) const d = await r.json() setOutput(d.output || '') load() } const CATEGORIES = ['api-key', 'token', 'password', 'certificate', 'ssh-key', 'general'] return (

Secrets Vault

AES-256-CBC encrypted · {secrets.length} secrets · {vaultExists ? '~/.blackroad/vault/' : 'vault not initialized'}

{/* Security notice */}

Secrets are encrypted at rest using AES-256-CBC. Master key stored at ~/.blackroad/vault/.master.key (chmod 400). Never commit vault contents.

{/* Add form */} {adding && (

Add New Secret

setForm(f => ({ ...f, name: e.target.value }))} placeholder="e.g. GITHUB_TOKEN" style={{ width: '100%', background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 8, padding: '9px 12px', color: '#fff', fontSize: 13, outline: 'none', boxSizing: 'border-box', fontFamily: 'monospace' }} />