Migration 20261012100000_schluessel_vorgaenge: Vorgangsarten SCHLUESSEL_ERZEUGEN, SCHLUESSEL_IMPORTIEREN, PRIVATE_KEY_LOESCHEN (für #014 gleich mit), CHECK schluessel_slug_gueltig (^[a-z0-9-]{1,64}$, der Slug landet in Dateinamen) und schluessel_geloescht_ohne_private_key.
Erzeugen und Paar-Import laufen im Worker (src/worker/ablaeufe/schluessel.ts). Das Web
prüft nur Metadaten (Slug frei, Person/Vorlage vorhanden, Ablauf nicht in der Vergangenheit)
und reiht den Vorgang ein. Passphrase und importierter Private Key reisen wie das
Bootstrap-Passwort mit SSHKV_APP_SECRET verschlüsselt im Job (geheim), nie in Vorgang.eingabe; der Worker löscht den Job danach. Der Worker prüft die Metadaten erneut.
Kategorie: Erzeugen und Paar-Import legen immer DIENST an. Personen-Keys speichert sshkv
nie mit Private Key (DB-CHECK), Deployment-Keys verwaltet der Worker selbst. Der
Public-Key-Import kennt PERSON und DIENST.
Passphrase: beim Erzeugen optional; gespeichert wird dann der passphrasengeschützte
OpenSSH-Key (passphraseGeschuetzt = true). Beim Import dient sie nur zum Lesen von Public Key
und Fingerprint und wird verworfen; gespeichert wird der Key so, wie er kam. Hinweis in der
Oberfläche: „Für automatische Anmeldungen (Host-zu-Host) ungeeignet.“
Typ:RSA4096 nur bei genau 4096 Bit, sonst ANDERER (z. B. RSA-3072, ecdsa). Die RSA-
Mindestlänge 2048 gilt beim Import.
Slug: aus dem Zweck abgeleitet (Umlaute gefaltet), bei Kollision -2, -3 …; ein
ausdrücklich gewünschter Kurzname muss frei sein. Slug, Typ und Kommentar sind danach nicht
mehr änderbar – sie stehen schon in Dateinamen und authorized_keys-Zeilen.
Status von Hand nur AKTIV ↔ ABGELAUFEN; ROTIERT/WIDERRUFEN setzen #023/#024,
Deployment-Keys sind ausgenommen (sonst erzeugte der Worker beim Start einen neuen).
Ablaufdatum ohne Angabe aus Vorlage.standardLaufzeitTage.
„Private Key gespeichert?“ liest das Web aus masterKeyVersion (der CHECK schluessel_umschlag_vollstaendig koppelt sie an das Chiffrat) – das Chiffrat wird im Web
nie selektiert.
Grenze abgesichert:tests/schluessel-grenze.test.ts verlangt für jeden zeilenliefernden prisma.schluessel.*-Aufruf außerhalb von src/worker ein select und verbietet schluessel: true als Relation; tests/schluessel.test.ts prüft, dass weder Vorgang,
Protokoll noch pg-boss-Job nach Erzeugen/Import den Key oder die Passphrase enthalten.
Platzhalter für #016/#017: src/app/(app)/schluessel/[id]/PublicAusrollenAktion.tsx und PrivateAusrollenAktion.tsx, Props { schluesselId: string; darf: boolean }, rendern null.
Personen-Keys zeigen im Detail nur die Karte „Public Key ausgerollt auf“ (eine Private-Karte
wäre immer leer).
Prototyp /schluessel/beispiel samt src/beispiel/schluessel.ts entfernt; die Übersicht
(noch Prototyp, #026) verlinkt auf /schluessel.
Quelle: docs/issues/012-schluessel-verwalten.md – dort wird dieses Issue gepflegt.
## Ziel
Schlüsselliste und -formular: erzeugen (ed25519, rsa-4096), importieren (Public oder Paar), exportieren; Kategorien, Tags, Ablaufdatum.
## Aufgaben
- [x] Liste mit Filter
- [x] Erzeugen-Dialog
- [x] Import mit Prüfung
- [x] Export Public Key
- [x] Schlüsseldetail-Gerüst mit Verwendungen
## Akzeptanzkriterien
- [x] Private Keys erscheinen nie in Listen-Antworten
## Abhängigkeiten
#006
## Notizen / Entscheidungen
- **Migration `20261012100000_schluessel_vorgaenge`:** Vorgangsarten `SCHLUESSEL_ERZEUGEN`,
`SCHLUESSEL_IMPORTIEREN`, `PRIVATE_KEY_LOESCHEN` (für #014 gleich mit), CHECK
`schluessel_slug_gueltig` (`^[a-z0-9-]{1,64}$`, der Slug landet in Dateinamen) und
`schluessel_geloescht_ohne_private_key`.
- **Erzeugen und Paar-Import laufen im Worker** (`src/worker/ablaeufe/schluessel.ts`). Das Web
prüft nur Metadaten (Slug frei, Person/Vorlage vorhanden, Ablauf nicht in der Vergangenheit)
und reiht den Vorgang ein. Passphrase und importierter Private Key reisen wie das
Bootstrap-Passwort mit `SSHKV_APP_SECRET` verschlüsselt im Job (`geheim`), nie in
`Vorgang.eingabe`; der Worker löscht den Job danach. Der Worker prüft die Metadaten erneut.
- **Kategorie:** Erzeugen und Paar-Import legen immer `DIENST` an. Personen-Keys speichert sshkv
nie mit Private Key (DB-CHECK), Deployment-Keys verwaltet der Worker selbst. Der
Public-Key-Import kennt `PERSON` und `DIENST`.
- **Passphrase:** beim Erzeugen optional; gespeichert wird dann der passphrasengeschützte
OpenSSH-Key (`passphraseGeschuetzt = true`). Beim Import dient sie nur zum Lesen von Public Key
und Fingerprint und wird verworfen; gespeichert wird der Key so, wie er kam. Hinweis in der
Oberfläche: „Für automatische Anmeldungen (Host-zu-Host) ungeeignet.“
- **Typ:** `RSA4096` nur bei genau 4096 Bit, sonst `ANDERER` (z. B. RSA-3072, ecdsa). Die RSA-
Mindestlänge 2048 gilt beim Import.
- **Slug:** aus dem Zweck abgeleitet (Umlaute gefaltet), bei Kollision `-2`, `-3` …; ein
ausdrücklich gewünschter Kurzname muss frei sein. Slug, Typ und Kommentar sind danach nicht
mehr änderbar – sie stehen schon in Dateinamen und authorized_keys-Zeilen.
- **Status von Hand** nur `AKTIV` ↔ `ABGELAUFEN`; `ROTIERT`/`WIDERRUFEN` setzen #023/#024,
Deployment-Keys sind ausgenommen (sonst erzeugte der Worker beim Start einen neuen).
- **Ablaufdatum** ohne Angabe aus `Vorlage.standardLaufzeitTage`.
- **„Private Key gespeichert?“** liest das Web aus `masterKeyVersion` (der CHECK
`schluessel_umschlag_vollstaendig` koppelt sie an das Chiffrat) – das Chiffrat wird im Web
nie selektiert.
- **Grenze abgesichert:** `tests/schluessel-grenze.test.ts` verlangt für jeden zeilenliefernden
`prisma.schluessel.*`-Aufruf außerhalb von `src/worker` ein `select` und verbietet
`schluessel: true` als Relation; `tests/schluessel.test.ts` prüft, dass weder Vorgang,
Protokoll noch pg-boss-Job nach Erzeugen/Import den Key oder die Passphrase enthalten.
- **Platzhalter** für #016/#017: `src/app/(app)/schluessel/[id]/PublicAusrollenAktion.tsx` und
`PrivateAusrollenAktion.tsx`, Props `{ schluesselId: string; darf: boolean }`, rendern `null`.
- Personen-Keys zeigen im Detail nur die Karte „Public Key ausgerollt auf“ (eine Private-Karte
wäre immer leer).
- Prototyp `/schluessel/beispiel` samt `src/beispiel/schluessel.ts` entfernt; die Übersicht
(noch Prototyp, #026) verlinkt auf `/schluessel`.
---
Quelle: `docs/issues/012-schluessel-verwalten.md` – dort wird dieses Issue gepflegt.
florian.wilde
added this to the v0.2.0 milestone 2026-10-12 15:12:39 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Ziel
Schlüsselliste und -formular: erzeugen (ed25519, rsa-4096), importieren (Public oder Paar), exportieren; Kategorien, Tags, Ablaufdatum.
Aufgaben
Akzeptanzkriterien
Abhängigkeiten
#006
Notizen / Entscheidungen
20261012100000_schluessel_vorgaenge: VorgangsartenSCHLUESSEL_ERZEUGEN,SCHLUESSEL_IMPORTIEREN,PRIVATE_KEY_LOESCHEN(für #014 gleich mit), CHECKschluessel_slug_gueltig(^[a-z0-9-]{1,64}$, der Slug landet in Dateinamen) undschluessel_geloescht_ohne_private_key.src/worker/ablaeufe/schluessel.ts). Das Webprüft nur Metadaten (Slug frei, Person/Vorlage vorhanden, Ablauf nicht in der Vergangenheit)
und reiht den Vorgang ein. Passphrase und importierter Private Key reisen wie das
Bootstrap-Passwort mit
SSHKV_APP_SECRETverschlüsselt im Job (geheim), nie inVorgang.eingabe; der Worker löscht den Job danach. Der Worker prüft die Metadaten erneut.DIENSTan. Personen-Keys speichert sshkvnie mit Private Key (DB-CHECK), Deployment-Keys verwaltet der Worker selbst. Der
Public-Key-Import kennt
PERSONundDIENST.OpenSSH-Key (
passphraseGeschuetzt = true). Beim Import dient sie nur zum Lesen von Public Keyund Fingerprint und wird verworfen; gespeichert wird der Key so, wie er kam. Hinweis in der
Oberfläche: „Für automatische Anmeldungen (Host-zu-Host) ungeeignet.“
RSA4096nur bei genau 4096 Bit, sonstANDERER(z. B. RSA-3072, ecdsa). Die RSA-Mindestlänge 2048 gilt beim Import.
-2,-3…; einausdrücklich gewünschter Kurzname muss frei sein. Slug, Typ und Kommentar sind danach nicht
mehr änderbar – sie stehen schon in Dateinamen und authorized_keys-Zeilen.
AKTIV↔ABGELAUFEN;ROTIERT/WIDERRUFENsetzen #023/#024,Deployment-Keys sind ausgenommen (sonst erzeugte der Worker beim Start einen neuen).
Vorlage.standardLaufzeitTage.masterKeyVersion(der CHECKschluessel_umschlag_vollstaendigkoppelt sie an das Chiffrat) – das Chiffrat wird im Webnie selektiert.
tests/schluessel-grenze.test.tsverlangt für jeden zeilenlieferndenprisma.schluessel.*-Aufruf außerhalb vonsrc/workereinselectund verbietetschluessel: trueals Relation;tests/schluessel.test.tsprüft, dass weder Vorgang,Protokoll noch pg-boss-Job nach Erzeugen/Import den Key oder die Passphrase enthalten.
src/app/(app)/schluessel/[id]/PublicAusrollenAktion.tsxundPrivateAusrollenAktion.tsx, Props{ schluesselId: string; darf: boolean }, rendernnull.wäre immer leer).
/schluessel/beispielsamtsrc/beispiel/schluessel.tsentfernt; die Übersicht(noch Prototyp, #026) verlinkt auf
/schluessel.Quelle:
docs/issues/012-schluessel-verwalten.md– dort wird dieses Issue gepflegt.