Erstatter .env-filer for API-nøkler. AES-256-GCM kryptert i PG, administrert via admin-UI, injisert av maskinrommet som env ved verktøy-spawning. Audit trail, test-tilkobling, flere nøkler per provider, deaktivering uten sletting. Ingen endring i verktøykode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.4 KiB
Nøkkelhåndtering — API-nøkler i PG, administrert via UI
Problemstilling
API-nøkler (OpenRouter, Anthropic, Gemini, xAI, OpenAI, ElevenLabs
etc.) ligger i klartekst i /srv/synops/.env. Ingen audit trail,
ingen granulær tilgang, lett å glemme å oppdatere.
Modell
Maskinrommet er nøkkelforvalter. Nøkler lagres kryptert i PG, administreres via admin-UI, injiseres i verktøy ved oppstart.
Admin-UI → maskinrommet → PG (kryptert)
↓
spawner verktøy med nøkler som env
↓
synops-agent, synops-ai, synops-clip osv.
Database-skjema
CREATE TABLE api_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
provider TEXT NOT NULL, -- 'openrouter', 'anthropic', 'gemini', 'xai', 'openai', 'elevenlabs'
label TEXT, -- valgfritt navn ("Vegards OpenRouter", "Prod Anthropic")
key_encrypted BYTEA NOT NULL, -- AES-256-GCM kryptert
key_hint TEXT, -- siste 4 tegn for identifisering ("...c08b")
is_active BOOLEAN DEFAULT true, -- deaktivert uten å slette
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now(),
created_by UUID REFERENCES nodes(id), -- hvem la inn nøkkelen
last_used TIMESTAMPTZ, -- sist brukt (oppdateres ved bruk)
usage_count BIGINT DEFAULT 0 -- antall ganger brukt
);
CREATE INDEX idx_api_keys_provider ON api_keys(provider, is_active);
Kryptering
Nøkkelen krypteres med AES-256-GCM. Krypteringsnøkkelen (master key) lever som:
- Env-variabel
SYNOPS_MASTER_KEYpå hosten - Eller i systemd credentials (LoadCredential)
- Aldri i PG, aldri i .env
// Maskinrommet krypterer ved lagring
let encrypted = aes_gcm_encrypt(&api_key, &master_key);
// Maskinrommet dekrypterer ved bruk
let api_key = aes_gcm_decrypt(&encrypted, &master_key);
Kun maskinrommet kjenner master key. Admin-UI sender klartekst til maskinrommet via HTTPS, maskinrommet krypterer og lagrer.
Nøkkeloppslag
Når et verktøy trenger en API-nøkkel:
// I maskinrommet, ved spawning av synops-agent:
let key = db::get_active_key("openrouter").await?;
cmd.env("OPENROUTER_API_KEY", key.decrypt(&master_key)?);
Verktøyet ser nøkkelen som vanlig env-variabel. Ingen endring i verktøykoden — de leser allerede fra env.
Alternativt for synops-agent (som kjører lenge): maskinrommet eksponerer et internt endepunkt:
GET /internal/api-key/{provider}
Authorization: Bearer <intern-token>
→ { "key": "sk-..." }
Admin-UI
Nøkkelliste
┌─ API-nøkler ──────────────────────────────────┐
│ │
│ Provider Label Status Sist │
│ OpenRouter Vegards OR ✅ Aktiv nå │
│ Anthropic Prod ✅ Aktiv 2t │
│ Gemini — ⚫ Inaktiv │
│ xAI — ⚫ Mangler │
│ OpenAI — ⚫ Mangler │
│ ElevenLabs Prod TTS ✅ Aktiv 1d │
│ │
│ [+ Legg til nøkkel] │
└────────────────────────────────────────────────┘
Legg til / rediger
┌─ Ny nøkkel ───────────────────────────────────┐
│ │
│ Provider: [OpenRouter ▼] │
│ Label: [Vegards OpenRouter ] │
│ Nøkkel: [sk-or-v1-... ] │
│ │
│ [Test tilkobling] [Lagre] [Avbryt] │
└────────────────────────────────────────────────┘
"Test tilkobling" gjør et minimalt API-kall (list models) for å verifisere at nøkkelen fungerer før lagring.
Sikkerhetsregler
- Nøkkelen vises aldri etter lagring — kun hint ("...c08b")
- For å endre: slett gammel, legg inn ny
- Deaktivering beholder nøkkelen men bruker den ikke
- Sletting fjerner den permanent
- Audit: created_by, created_at, last_used, usage_count
Flere nøkler per provider
En provider kan ha flere nøkler (f.eks. ulike OpenRouter-kontoer):
SELECT key_encrypted FROM api_keys
WHERE provider = 'openrouter' AND is_active = true
ORDER BY usage_count ASC -- round-robin / minst brukt
LIMIT 1
Gir enkel lastbalansering og mulighet for å rotere nøkler uten nedetid.
Migrering fra .env
- Admin legger inn nøkler via UI
- Maskinrommet bruker PG-nøkler i stedet for env
- .env-filen beholder nøklene som fallback
- Når alt fungerer: fjern nøkler fra .env
- Beholder kun
SYNOPS_MASTER_KEYsom env
Hva som endres
| Komponent | Endring |
|---|---|
| PG | Ny api_keys-tabell |
| Maskinrommet | Kryptering, nøkkeloppslag, API-endepunkt |
| Admin-UI | Ny side for nøkkelhåndtering |
| synops-agent | Ingen (leser fortsatt fra env) |
| CLI-verktøy | Ingen (leser fortsatt fra env) |
| .env | Nøkler fjernes gradvis |
Prioritet
Middels. Fungerer med .env nå. Viktigere når flere brukere skal administrere egne nøkler (BYOK-modell) eller når nøkler roteres jevnlig.