Ein einziges POSIX-Skript fuer beide Wege: der Worker fuehrt es nach einer einmaligen Passwortanmeldung aus, und genau dasselbe Skript steht als Befehl zum Kopieren bereit. So gibt es keine zwei Wahrheiten darueber, was ein Bootstrap auf dem Host aendert. Das Passwort reist nur verschluesselt im Job und steht nie in Vorgang, Protokoll oder Log. Dateien im Home des Zielbenutzers liest und schreibt das Skript als dieser Benutzer: ein Symlink auf /etc/shadow fuehrt zum Abbruch statt zur Kopie (Integrationstest). sudoers wird erst nach visudo -c aktiv. Am Ende beweist eine Anmeldung per Deployment-Key, dass sshkv ohne Passwort weiterarbeiten kann. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
5.1 KiB
Markdown
113 lines
5.1 KiB
Markdown
# Konventionen für SSHKeyVault (`sshkv`)
|
||
|
||
Diese Datei ist verbindlich. Plan und Begründungen: `docs/PLAN.md`. Arbeitsweise wie in
|
||
Laufzettel und ProxLedger.
|
||
|
||
## Arbeitsweise
|
||
|
||
- Issues liegen als Dateien in `docs/issues/NNN-slug.md` und sind die Quelle der Wahrheit;
|
||
`pnpm issues:sync` überträgt sie nach Gitea (#030).
|
||
- Je Issue ein Branch `phase/NNN-slug`, Merge nach `main` mit `--no-ff`.
|
||
- Conventional Commits auf Deutsch, Betreff ≤ 72 Zeichen, Umlaute im Betreff gefaltet,
|
||
`(#NNN)` am Ende. Der Body erklärt das **Warum**.
|
||
- `DEVLOG.md`: Zeit je Issue und Meilenstein, Sitzungen mit „Erledigt“ und „Entscheidungen“.
|
||
- `CHANGELOG.md`: Keep a Changelog, deutsch. Tag `vX.Y.Z` je Meilenstein; Version in `package.json`.
|
||
|
||
## Sprache
|
||
|
||
**Deutsch, überall wo ein Mensch liest oder benennt** – auch Bezeichner (Funktionen, Variablen,
|
||
Prisma-Modelle und -Felder, Enum-Werte, Dateinamen, Tests). **Umlaute in Bezeichnern werden
|
||
ASCII-gefaltet** (`Schluessel`, `geaendertAm`). Oberflächentexte, Fehlermeldungen und
|
||
Protokoll-Aktionen schreiben sich richtig („Schlüssel widerrufen“).
|
||
|
||
Englisch bleiben Framework-Namen (`page.tsx`, `route.ts`, `GET`) und Fachjargon
|
||
(`Host`, `Fingerprint`, `Token`, `OIDC`, `authorized_keys`, `Public Key`, `Private Key`, `Drift`).
|
||
|
||
Kommentare erklären **warum**, nicht was, und tragen wo hilfreich die Issue-Nummer `(#007)`.
|
||
|
||
## Namensabbildung zum Plan
|
||
|
||
| Plan | Modell | Plan | Modell |
|
||
|---|---|---|---|
|
||
| User | `Benutzer` | PublicKeyUsage | `PublicVerwendung` |
|
||
| Person | `Person` | PrivateKeyUsage | `PrivateVerwendung` |
|
||
| Customer | `Kunde` | ManagedAccount | `Zielkonto` |
|
||
| Host | `Host` | Template | `Vorlage` |
|
||
| HostGroup | `Hostgruppe` | Observation | `Beobachtung` |
|
||
| HostKey | `HostSchluessel` | DriftFinding | `Abweichung` |
|
||
| SshKey | `Schluessel` | Operation | `Vorgang` |
|
||
| AuditEvent | `Protokoll` | Setting | `Einstellung` |
|
||
|
||
## Schichtung
|
||
|
||
```
|
||
src/server/services/* Regeln und Schreiboperationen.
|
||
src/server/queries/* Lesemodelle. Wenden die Rolle selbst an.
|
||
src/server/actions/* Dünne "use server"-Einstiege: Recht prüfen, zod, Service rufen.
|
||
src/server/auth/* Anmeldung, Sitzung, Rechte, Re-Auth.
|
||
src/lib/* Framework-freie Helfer (kein Next, kein React, kein Prisma) – gut testbar.
|
||
src/lib/ssh/* OpenSSH-Formate, authorized_keys-Parser, Fingerprints. Rein.
|
||
src/worker/* Alles, was SSH spricht oder den Master-Key braucht. Läuft nur im Worker.
|
||
src/components/* Darstellung, keine Geschäftslogik.
|
||
```
|
||
|
||
## Feste Regeln
|
||
|
||
- **Private Keys** erscheinen nie in Logs, Fehlermeldungen, Listenantworten, Props von
|
||
Client-Komponenten oder im Protokoll. Prisma-Abfragen auf `Schluessel` nutzen feste
|
||
`select`-Listen; `privatVerschluesselt` liest nur `src/worker/geheimnisse.ts`.
|
||
- **Master-Key nur im Worker.** Der Webprozess importiert nichts aus `src/worker/*`.
|
||
- **Kein Klartext-Passwort** (Bootstrap) in Datenbank, Job-Payload, Log oder Protokoll.
|
||
- **Host-Keys werden strikt geprüft.** Es gibt keinen Codepfad, der einen geänderten Host-Key
|
||
automatisch annimmt.
|
||
- **Rechte** nur serverseitig über `darf()`; jede Seite und jeder Route Handler prüft selbst.
|
||
Rolle wird pro Anfrage aus der Datenbank gelesen, nicht aus dem Token.
|
||
- **Protokoll** ist append-only (Trigger) und über eine Hash-Kette verbunden.
|
||
- Zod immer über `@/lib/validierung` importieren (deutsche Meldungen).
|
||
|
||
## Werkzeuge und Versionen
|
||
|
||
Node 24 (`.nvmrc`), pnpm 12. Exakt gepinnt: `next@16.3.6`, `react@19.3.0`,
|
||
`prisma`/`@prisma/client`/`@prisma/adapter-pg` `7.10.0`, `vitest@5.0.1`, `vite@8.3.0`,
|
||
`@playwright/test@1.63.0`. `eslint` bleibt auf 9.x, `typescript` auf `~6.0.3`
|
||
(Gründe siehe Laufzettel/CLAUDE.md).
|
||
|
||
**Next 16:** `middleware.ts` heißt `proxy.ts`. `cookies()`, `headers()`, `params`,
|
||
`searchParams` sind async.
|
||
|
||
**Prisma 7:** Generator `prisma-client` nach `src/generated/prisma`, Zugriff über
|
||
`@prisma/adapter-pg`, Lazy-Proxy in `src/lib/db.ts`. Typen aus `@/generated/prisma/client`,
|
||
Enum-Werte aus `@/generated/prisma/enums`.
|
||
|
||
## Entwicklung
|
||
|
||
```bash
|
||
pnpm db:up # PostgreSQL auf 127.0.0.1:5436
|
||
pnpm db:migrate && pnpm db:seed
|
||
pnpm dev # http://localhost:3400
|
||
pnpm worker # zweites Terminal
|
||
```
|
||
|
||
Oberflächen immer gegen den **gebauten Stand** prüfen (`pnpm build && pnpm start`) – siehe
|
||
Laufzettel: `next dev` hydriert in dieser Umgebung nicht zuverlässig.
|
||
|
||
## Tests brauchen die Testdatenbank für sich allein
|
||
|
||
`tests/hilfen/db.ts` leert vor jedem Test alle Tabellen. Zwei gleichzeitige Läufe (z. B. ein
|
||
Sub-Agent und die Hauptsitzung) räumen sich gegenseitig die Daten weg – das sieht aus wie ein
|
||
echter Fehler („No record was found for an update“). Parallel Prüfende bekommen deshalb eine
|
||
**eigene** Datenbank:
|
||
|
||
```bash
|
||
docker exec sshkv-dev-db psql -U sshkv -c "create database sshkv_test_<name>"
|
||
TEST_DATABASE_URL=postgresql://sshkv:sshkv@localhost:5436/sshkv_test_<name>?schema=public pnpm test
|
||
```
|
||
|
||
## Vor jedem Commit
|
||
|
||
```bash
|
||
pnpm lint && pnpm typecheck && pnpm format:pruefen && pnpm test
|
||
```
|
||
|
||
Vor jedem Merge zusätzlich `pnpm test:integration` (braucht Docker).
|