docs: Architekturplan, Issues und Projektorganisation anlegen

Plan für ProxDeploy zur Freigabe: Architektur (app, worker, Postgres),
Prisma-Entwurf, REST-API, Dateistruktur und sechs Etappen.

Die Proxmox-API wurde gegen das Schema von PVE 9.2 und die Quellen der
8er-Zweige geprüft. Daraus folgt die Fähigkeitserkennung pro Server:
Template-Bau per API erst ab 8.4, Snippets und LXC-Exec nur per SSH.

Refs #001

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 09:05:57 +02:00
co-authored by Claude Opus 5.5
commit 6c16d56960
25 changed files with 1153 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
node_modules/
.next/
out/
dist/
coverage/
*.tsbuildinfo
next-env.d.ts
src/generated/
.env
.env.*
!.env.example
cli/bin/
playwright-report/
test-results/
.DS_Store
+20
View File
@@ -0,0 +1,20 @@
# Changelog
Alle nennenswerten Änderungen an ProxDeploy werden in dieser Datei dokumentiert.
Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.1.0/),
und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
## [Unveröffentlicht]
### Hinzugefügt
- Architektur- und Umsetzungsplan (`docs/architektur.md`) mit Prüfung der Proxmox-API 8.x/9.x und der Image-Quellen (#001)
- Issues #001–#020 für die sechs Etappen und den Feldtest (#001)
### Geändert
### Behoben
### Entfernt
### Sicherheit
+37
View File
@@ -0,0 +1,37 @@
# DEVLOG – ProxDeploy
## Zeitübersicht
**Gesamt:** 0 h 25 min (laufende Sitzung)
Zeiten stammen aus den Git-Commit-Zeitstempeln je Issue; Wartezeit auf Rückmeldungen ist enthalten.
| Issue | Zeit |
|---|---|
| #001 Projekt-Setup | 0 h 25 min (laufend) |
| Meilenstein | Zeit |
|---|---|
| v0.1.0 | – |
---
## 2026-10-12 · Sitzung 1 · 08:45–laufend
**Issue:** #001
**Version:** 0.1.0-dev
### Erledigt
- Auftrag gelesen, Rückfragen gestellt: Reverse-Proxy (ja), OIDC (implementieren, vorerst nur lokal testen), Testumgebung (folgt), CLI (Go)
- Umgebung geprüft: Docker 29 vorhanden, Node und Go fehlen → Builds und Tests laufen im Container
- Proxmox-API 8.x/9.x gegen API-Schema 9.2, Quellen der stable-8-Zweige und Release Notes geprüft (Ergebnis: `docs/architektur.md` Abschnitt 3)
- Image-URLs, Prüfsummen-Dateien, LXC-Template-Namen, Docker- und Zabbix-Repos per Abruf geprüft
- Git-Repository (`main`), CHANGELOG, DEVLOG, Architekturplan und 20 Issues angelegt
### Entscheidungen
- **VM-Provisionierung immer über den Guest-Agent:** funktioniert ohne SSH und liefert Live-Fortschritt. Snippets braucht es nur beim Template-Bau.
- **Guest-Agent wird ins Template gebaut:** keines der vier Cloud-Images bringt ihn mit, und ohne Agent gibt es bei DHCP keine IP und keine Provisionierung.
- **LXC braucht für Profile immer SSH** (zum Node oder in den Container): Proxmox hat keine Exec-API für Container.
- **Template-Bau per API erst ab PVE 8.4** (`download-url` mit `content=import`); davor über SSH.
- **Planer und Ausführung getrennt:** der Dry-Run zeigt exakt den Plan, den der worker danach abarbeitet.
- **CLI in Go** mit aus OpenAPI generiertem Client.
- **Deutsche Bezeichner** wie in ProxLedger und Laufzettel; YAML-Schlüssel im Katalog bleiben englisch (Vorgabe).
+602
View File
@@ -0,0 +1,602 @@
# ProxDeploy – Architektur- und Umsetzungsplan
Stand: 2026-10-12 · Version 0.1.0-dev · **Entwurf zur Freigabe**
## 1. Überblick
```
Browser (Web-GUI) Terminal (proxdeploy, Go)
│ Cookie │ Bearer-Token
└──────────┬──────────────┘
▼ REST /api/v1 + SSE
┌────────────────────────────────────────────────────────────────────────┐
│ ProxDeploy (Docker Compose oder Debian-LXC), hinter Reverse-Proxy │
│ ┌──────────────────────┐ pg-boss ┌────────────────────────────┐ │
│ │ app (Next.js) │──Auftrag────▶│ worker (Node) │ │
│ │ UI · REST-API · SSE │◀─LISTEN/─────│ Engine: Planer, Phasen, │ │
│ │ Dry-Run (Planer) │ NOTIFY │ Proxmox-Client, Adapter │ │
│ └──────────┬───────────┘ └──────┬───────────┬─────────┘ │
│ └────────── PostgreSQL ───────────┘ │ │
└───────────────────────────────────────────────────────────┼─────────────┘
HTTPS :8006 (Token, Fingerprint-Pin) │ optional SSH
▼
Proxmox VE 8.x / 9.x (Einzelhost oder Cluster)
│ Guest-Agent / SSH in den Gast
▼
VM oder LXC (Debian, Ubuntu, …)
```
- **Ein Backend, zwei Oberflächen.** Die Engine (`src/engine/`) ist frameworkfrei und enthält die
gesamte Deploy-Logik. Die Web-GUI und die CLI sprechen ausschließlich die REST-API; die GUI hat
keinen Sonderweg an der API vorbei (Server Actions rufen dieselben Services wie die Route Handler).
- **app** liefert UI, REST-API, OpenAPI-Spezifikation und SSE-Ströme. Sie führt nie ein Deployment
aus, nur den **Planer** für den Dry-Run (reine Funktion, keine schreibenden Proxmox-Aufrufe).
- **worker** ist derselbe Codestand als eigener Prozess und arbeitet Aufträge aus der pg-boss-Queue ab.
Jede Phasenänderung wird in die DB geschrieben und per `NOTIFY auftrag_<id>` gemeldet; die app hängt
sich mit `LISTEN` daran und streamt an alle offenen SSE-Verbindungen. Ein Neuladen der Seite oder ein
Wiederverbinden der CLI liest zuerst den Stand aus der DB und setzt dann ab `Last-Event-ID` fort.
- **PostgreSQL** ist die einzige Datenhaltung (inkl. Queue). Keine Dateien außer dem Katalog im Image.
## 2. Tech-Stack
| Bereich | Wahl | Begründung |
|---|---|---|
| Web/API | Next.js 16 (App Router), React 19, TypeScript strict | Vorgabe; wie Laufzettel/ProxLedger |
| Styling | Tailwind CSS 4, Tokens als CSS-Variablen unter `@theme`, eigene Komponenten | Vorgabe, kein Admin-Template |
| Schriften | Space Grotesk, JetBrains Mono über `@fontsource-variable`, selbst gehostet | keine Google-Anfragen aus dem Intranet |
| Icons | Lucide (Stroke) + die Statussymbole ✓ ◐ ✗ ○ als Text | Status nie nur Farbe |
| DB | PostgreSQL 17, Prisma 7 mit `@prisma/adapter-pg` | Vorgabe |
| Jobs | pg-boss auf derselben Datenbank | Vorgabe; kein Redis nötig |
| Validierung | zod (Request, Katalog-YAML, Profil-Editor) | eine Schemaquelle für API, OpenAPI und Katalog |
| OpenAPI | aus den zod-Schemas erzeugt (`/api/v1/openapi.json`) | Go-Client wird daraus generiert |
| Auth | `jose` (Sitzungs-Cookie), `openid-client` (OIDC, PKCE), argon2id | wie ProxLedger; kein Auth.js-Beta |
| SSH | `ssh2` (Node) | Snippets schreiben, `pct exec`, SSH in den Gast |
| YAML | `yaml` (eemeli) | erhält Kommentare im Profil-Editor |
| Logs | `pino` (JSON auf stdout), zusätzlich pro Auftrag in der DB | Betrieb + GUI |
| Tests | Vitest (Engine, API mit echter Test-DB), Playwright (Assistent, Live-Ansicht, 390 px) | |
| CLI | **Go**, statisches Binary | siehe Abschnitt 9 |
Exakte Versionen werden in #001 gegen npm/Go-Proxy geprüft und **gepinnt** (abgelagerte Fassungen,
keine RCs) und in `CLAUDE.md` festgehalten.
## 3. Was geht per Proxmox-API, was braucht SSH
Geprüft am 2026-10-12 gegen das API-Schema von PVE 9.2 (`apidoc.js`), die Quellen der Zweige
`stable-8`/`stable-bookworm` (pve-storage, qemu-server, pve-container) und die Release Notes
8.0–9.2. Belege stehen in den Issues #005 und #007.
| Fähigkeit | PVE 8.0–8.3 | PVE 8.4+ / 9.x | Weg |
|---|---|---|---|
| Cloud-Image herunterladen + Checksumme prüfen (`download-url`, `content=import`, qcow2/raw/vmdk) | **nein** (nur iso/vztmpl, OVA ab 8.2) | ja | API |
| Disk aus heruntergeladenem Image anlegen (`scsi0: <stor>:0,import-from=<stor>:import/<datei>`) | nur per absolutem Pfad → nur `root@pam` | ja, per Volume-ID | API |
| Klonen, Konfiguration, cloud-init (`ciuser`, `sshkeys`, `ipconfigN`, `nameserver`, `searchdomain`, `ciupgrade`), Start/Stopp, Template | ja | ja | API |
| Guest-Agent `exec`, `exec-status`, `file-write` (max. 60 KiB je Aufruf), `network-get-interfaces` | ja (`VM.Monitor`) | ja (`VM.GuestAgent.*` ab 9.0) | API |
| LXC anlegen (`ssh-public-keys`, `net0` mit `tag`, `unprivileged`, `features: nesting`) | ja | ja | API |
| LXC-Template-Liste und -Download (`aplinfo`) | ja | ja | API |
| LXC-IP auslesen (`lxc/{id}/interfaces`) | ja | ja | API |
| LXC-Features außer `nesting` (`keyctl`, `fuse`, …) | nur `root@pam` per Ticket, **kein Token** | dito | SSH (`pct set`) |
| **Befehle im LXC ausführen** | **keine API** | **keine API** | SSH (`pct exec` auf dem Node **oder** SSH in den Container) |
| **cloud-init-Snippets schreiben** (`cicustom`) | **keine API** (Upload nur iso/vztmpl/import) | dito | SSH |
| Zertifikats-Fingerprint | `nodes/{n}/certificates/info` | dito | API (nach erstem Pin) |
**Folgerungen für die Engine:**
1. **Fähigkeitserkennung pro Server** (`src/engine/proxmox/faehigkeiten.ts`), beim Verbindungstest und
vor jedem Auftrag aufgefrischt: PVE-Version je Node, Storages mit Inhaltstyp `import`, `snippets`,
`vztmpl`, `images`/`rootdir`, Rechte des Tokens (`GET /access/permissions`), SSH konfiguriert und
erreichbar. Daraus ergibt sich pro Server und Vorgang die **Strategie**, die in der GUI sichtbar ist
(„Template-Bau: per API“ / „braucht SSH, weil PVE 8.3“).
2. **VM-Provisionierung läuft immer über den Guest-Agent** (Skript per `file-write` in 60-KiB-Stücken,
dann `exec` + `exec-status`-Polling). Damit funktioniert sie **ohne SSH** und liefert Live-Fortschritt.
Standard-cloud-init von Proxmox liefert nur Benutzer, Keys und Netz; `ciupgrade=0`, Updates macht
das Profil `base` (sichtbar als eigene Phase statt stummem Boot-Upgrade).
3. **Der Guest-Agent ist in keinem der vier Cloud-Images vorinstalliert** (geprüft in den Paketlisten).
Deshalb wird er **beim Template-Bau** eingebaut, damit jeder Klon sofort einen Agent hat:
- mit SSH zum Node: einmaliger Vorbereitungsstart mit `cicustom: vendor=<snippet>` (installiert
`qemu-guest-agent`, `cloud-init clean`, `poweroff`) – funktioniert auch mit DHCP;
- ohne SSH: Vorbereitungsstart mit einer **Bau-Adresse** (statische IP aus den Server-Einstellungen)
und dem ProxDeploy-Bereitstellungsschlüssel per `sshkeys`, dann SSH **in die VM**, dieselben Schritte.
- Danach `cloud-init clean`, Maschinen-ID leeren, als Template markieren.
4. **LXC-Provisionierung** braucht immer SSH – bevorzugt `pct exec` über SSH zum Node (kein Netzweg
zum Container nötig), sonst SSH direkt in den Container (Bereitstellungsschlüssel per
`ssh-public-keys`, IP aus Konfiguration bzw. `lxc/{id}/interfaces`). Das `debian`-Standard-Template
bringt `openssh-server` mit; der Bereitstellungsschlüssel wird am Ende wieder entfernt.
5. **Ohne jedes SSH** sind möglich: VM aus vorhandenem oder per API gebautem Template (PVE ≥ 8.4) mit
allen Profilen; LXC ohne Profile (nur Anlegen + Start). Die GUI graut nicht Mögliches mit Begründung aus.
**SSH-Zugang** pro Server: ProxDeploy erzeugt ein ed25519-Schlüsselpaar, zeigt den öffentlichen
Schlüssel samt Einzeiler zum Hinterlegen, pinnt den Host-Key beim ersten Test (Bestätigung durch den
Admin). Benutzer `root` auf dem Node (für `pct` und `/var/lib/vz/snippets` nötig).
## 4. Proxmox-Rechte (Rolle `ProxDeploy`)
Wird im README ausführlich dokumentiert, inkl. `pveum`-Befehlen.
- **Gemeinsam:** `VM.Allocate VM.Clone VM.Audit VM.Config.Disk VM.Config.CDROM VM.Config.CPU
VM.Config.Memory VM.Config.Network VM.Config.HWType VM.Config.Options VM.Config.Cloudinit
VM.PowerMgmt VM.Console Datastore.AllocateSpace Datastore.AllocateTemplate Datastore.Audit SDN.Use
SDN.Audit Sys.Audit Pool.Audit`
- **nur 8.x:** `VM.Monitor` (Guest-Agent); vor 8.2 statt `Sys.AccessNetwork` → `Sys.Modify`
- **8.2+ und 9.x:** `Sys.AccessNetwork` auf `/nodes/<node>` (für `download-url`)
- **nur 9.x:** `VM.GuestAgent.Audit VM.GuestAgent.Unrestricted` (ersetzt `VM.Monitor`)
- **optional:** `Datastore.Allocate` (heruntergeladene Images/Templates wieder löschen),
`Sys.Modify` (privilegierte Container ab 9.0)
- ACL: `/vms` oder ein Pool, `/storage/<id>`, `/sdn/zones/localnetwork` (bzw. konkrete Zonen), `/`
nur für `Sys.Audit`. Der Verbindungstest gleicht die effektiven Rechte gegen diese Liste ab und
nennt fehlende Rechte einzeln.
## 5. Distributionen, Adapter, Profile
### Katalog als Daten
`catalog/distros/*.yaml` und `catalog/profiles/*.yaml` liegen im Repo und im Image. Der worker
synchronisiert sie beim Start in die DB (Upsert über `id`, Hash der Datei; Aktiv-Schalter und eigene
Profile bleiben unangetastet). Die **YAML-Schlüssel bleiben englisch** wie in der Vorgabe – sie sind
ein Austauschformat, kein Code-Bezeichner. Validiert mit zod; ein fehlerhafter Eintrag wird mit
Dateiname und Pfad gemeldet und übersprungen, nicht der Start abgebrochen.
Mitgeliefert (URLs am 2026-10-12 per Abruf geprüft): Debian 12, Debian 13, Ubuntu 22.04, Ubuntu 24.04.
Ubuntu 26.04 LTS ist ebenfalls verfügbar (Cloud-Image und LXC-Template) – **Vorschlag: mitliefern**.
- Debian: `cloud.debian.org/images/cloud/<codename>/latest/debian-<n>-genericcloud-amd64.qcow2`,
`SHA512SUMS` (Zeilenformat `<hash>␣␣<datei>`), Benutzer `debian`
- Ubuntu: `cloud-images.ubuntu.com/<codename>/current/<codename>-server-cloudimg-amd64.img` (qcow2),
`SHA256SUMS` (Zeilenformat `<hash>␣*<datei>`), Benutzer `ubuntu`
- LXC: alle `*-standard_*_amd64.tar.zst`; bei mehreren Treffern (Debian 13: `13.1-2` und `13.6-1`)
gewinnt die höchste Version (Versionssortierung).
Ergänzende Felder gegenüber der Vorgabe: `codename`, `arch` (vorerst `amd64`), `vm.bios_default`
(`ovmf`/`seabios`), `vm.template_vmid_offset` (z. B. Debian 13 → 9013), `vm.min_disk_gb`.
### Provisionierungs-Adapter
`src/engine/adapter/schnittstelle.ts` definiert `FamilienAdapter` – jede Methode liefert ein
**idempotentes Shell-Fragment** (kein Ausführen im Adapter, damit testbar und im Dry-Run anzeigbar):
```ts
interface FamilienAdapter {
familie: string; // "debian"
aktualisieren(): Skript;
paketeInstallieren(pakete: string[]): Skript;
dienstAktivieren(name: string, starten?: boolean): Skript;
repoHinzufuegen(repo: PaketQuelle): Skript; // Schlüssel-URL, URIs, Suites, Komponenten
zeitzoneSetzen(tz: string): Skript;
localeSetzen(locale: string): Skript;
tastaturSetzen(layout: string): Skript;
benutzerAnlegen(b: Benutzerangaben): Skript; // inkl. sudo, authorized_keys
sshHaerten(o: SshHaertung): Skript; // PasswordAuthentication no, PermitRootLogin no
gastAgentInstallieren(): Skript;
automatischeUpdates(an: boolean): Skript;
fakten(): Skript; // gibt OS-Fakten (codename, ID) als JSON aus
}
```
Registriert in `src/engine/adapter/index.ts`. Ein neuer Adapter (`rhel`, `alpine`, `suse`) ist eine
neue Datei + ein Eintrag in der Registry + Tests; Katalog und Profile verweisen per `family` darauf.
### Profile
Ein Profil ist eine Liste deklarativer **Schritte** je Familie, die der Adapter in Shell übersetzt.
Für Sonderfälle gibt es `run` (rohes Shell, ausdrücklich nicht portabel). Variablen werden als
Formularfelder angezeigt und mit `{{ var.name }}` eingesetzt (eigene kleine Template-Funktion,
nur Ersetzung, keine Logik; Werte werden für Shell gequotet).
```yaml
id: zabbix-agent
name: Zabbix Agent 2
description: Zabbix Agent 2 (7.0 LTS) aus dem offiziellen Repo
requires: [base]
variables:
- { name: server, label: Zabbix-Server, type: string, required: true }
- { name: server_active, label: ServerActive, type: string, default: "{{ var.server }}" }
lxc: {} # z. B. docker: { features: { nesting: true } }
families:
debian:
- repo_deb: "https://repo.zabbix.com/zabbix/7.0/{{ os.id }}/pool/main/z/zabbix-release/zabbix-release_latest_7.0+{{ os.id }}{{ os.version_id }}_all.deb"
- packages: [zabbix-agent2]
- file: { path: /etc/zabbix/zabbix_agent2.d/proxdeploy.conf, content: "Server={{ var.server }}\nServerActive={{ var.server_active }}\nHostname={{ guest.name }}\n" }
- service: { name: zabbix-agent2, enable: true, restart: true }
```
Fehlt `families.<familie>` für die gewählte Distribution, ist das Profil im Assistenten ausgegraut
(„keine Umsetzung für Familie rhel“). Eigene Profile werden im YAML-Editor angelegt, gegen dasselbe
zod-Schema geprüft und mit `origin: custom` gespeichert; Katalog-Profile sind schreibgeschützt,
lassen sich aber als Kopie bearbeiten.
Mitgeliefert: `base`, `docker` (Docker CE, deb822 `docker.sources`, Key `/etc/apt/keyrings/docker.asc`,
Pakete `docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin`; LXC:
`nesting=1`), `zabbix-agent` (7.0 LTS; 8.0 hat noch keine Agent-Pakete).
## 6. Engine: Aufträge und Phasen
```
src/engine/
proxmox/ client.ts (fetch + undici, Fingerprint-Pinning), endpunkte/*.ts, faehigkeiten.ts, aufgaben.ts (UPID warten)
ssh/ verbindung.ts, node.ts (pct exec, Snippet schreiben), gast.ts
katalog/ schema.ts (zod), laden.ts, pruefsumme.ts (beide Zeilenformate)
adapter/ schnittstelle.ts, debian.ts, index.ts
profile/ aufloesen.ts (Reihenfolge, requires, Variablen), rendern.ts (→ Skript)
planer/ vm.ts, lxc.ts, template.ts → Plan { phasen[], aufrufe[] } (rein, keine I/O außer Lesen)
ablauf/ ausfuehren.ts (Phasen-Runner), ereignisse.ts (Senke-Interface), fehler.ts (Ursache + Hinweis)
transport/ gastagent.ts, ssh-node.ts, ssh-gast.ts → gemeinsames Interface `Ausfuehrer`
sperren.ts VMID-Reservierung
```
**Planer und Ausführung trennen.** Der Planer bekommt eine validierte Anfrage + einen *lesenden*
Proxmox-Zugriff + Fähigkeiten und liefert den Plan: die Phasen und je Phase die geplanten
API-Aufrufe (`POST /nodes/pve01/qemu/9013/clone newid=120 full=1 …`). Der Dry-Run zeigt genau diesen
Plan; die Ausführung arbeitet ihn ab. So können Dry-Run und Wirklichkeit nicht auseinanderlaufen.
**Phasen** (VM): Prüfungen → Template (übersprungen, wenn vorhanden) → Klonen → Hardware & Disk →
cloud-init → Start → Guest-Agent → Provisionierung (je Profil eine Unterzeile im Log) → Abschluss.
LXC: Prüfungen → LXC-Template → Anlegen → Start → Netzwerk → Provisionierung → Abschluss.
Jede Phase schreibt `status`, `detail` (der Muted-Text der CLI), `dauer`, bei Fehler `ursache` und
`hinweis`. **Web und CLI rendern nur diese Felder** – kein Text wird in einem Client erfunden.
**VMID-Vergabe ohne Doppelungen:** `pg_advisory_xact_lock(hash(serverId))`, dann nächste freie ID
aus `/cluster/resources` ∪ Tabelle `VmidReservierung`, Reservierung schreiben, Sperre fällt mit der
Transaktion. Die Reservierung gilt bis Auftragsende (bzw. Ablauf nach 2 h als Sicherung). Beim
`create`/`clone` wird ein Konflikt (`already exists`) einmal mit neuer ID wiederholt.
**Fehler und Aufräumen:** Ein Fehler beendet den Auftrag mit Status `FEHLGESCHLAGEN`, die Phase trägt
Ursache und Lösungshinweis (z. B. „Token fehlt VM.GuestAgent.Unrestricted auf /vms/120 → Rolle
ergänzen“). Was angelegt wurde, steht in `Auftrag.angelegt` (VMID, heruntergeladene Volumes).
„Aufräumen“ ist ein eigener Auftrag (`AUFRAEUMEN`), nur nach Bestätigung, und löscht nur, was
ProxDeploy selbst angelegt hat (geprüft über das Tag `proxdeploy` + Auftrags-ID in der Beschreibung).
**Parallelität:** pg-boss mit konfigurierbarer Parallelität im worker (Standard 4); Template-Bau pro
Server+Distribution singleton (zweiter Deploy wartet auf denselben Bau statt doppelt zu bauen).
## 7. Datenmodell (Prisma, Entwurf)
Namensabbildung (verbindlich, kommt nach `CLAUDE.md`):
| Vorgabe | Modell / Begriff | Vorgabe | Modell / Begriff |
|---|---|---|---|
| User | `Benutzer` | Deployment / Job | `Auftrag` (Art `AUSROLLEN`, `TEMPLATE_BAUEN`, `AUFRAEUMEN`) |
| API token (CLI) | `ApiToken` | Phase | `Phase` |
| SSH key | `SshSchluessel` | Job log line | `Logeintrag` |
| Server | `Server` | VMID lock | `VmidReservierung` |
| Node | `Knoten` (nur Feldname, kein Modell) | Template (Proxmox) | `Template` |
| Distribution | `Distribution` | Preset | `Vorlage` |
| Profile | `Profil` | Audit log | `Protokoll` |
| Setting | `Einstellung` | Key provider | `SchluesselQuelle` (Interface) |
```prisma
enum Rolle { ADMIN OPERATOR }
enum GastTyp { VM LXC }
enum AuftragsArt { AUSROLLEN TEMPLATE_BAUEN AUFRAEUMEN }
enum AuftragsStatus { WARTEND LAEUFT ERFOLGREICH FEHLGESCHLAGEN ABGEBROCHEN AUFGERAEUMT }
enum PhasenStatus { OFFEN LAEUFT ERLEDIGT FEHLER UEBERSPRUNGEN }
enum Herkunft { KATALOG EIGEN }
enum Quelle { WEB CLI API }
model Benutzer {
id String @id @default(cuid())
benutzername String @unique
anzeigename String
email String?
passwortHash String? // null bei reinen OIDC-Benutzern
oidcSubjekt String? @unique
rolle Rolle @default(OPERATOR)
aktiv Boolean @default(true)
standards Json? // persönliche Standardwerte im Assistenten
erstelltAm DateTime @default(now())
apiTokens ApiToken[] sshSchluessel SshSchluessel[] auftraege Auftrag[] protokoll Protokoll[]
}
model ApiToken {
id String @id @default(cuid())
benutzerId String
name String
praefix String @unique // "pd_ab12cd34" – erkennbar, Rest nur gehasht
hash String // SHA-256 des Geheimnisses
zuletztGenutztAm DateTime?
ablaufAm DateTime?
widerrufenAm DateTime?
erstelltAm DateTime @default(now())
}
model SshSchluessel {
id String @id @default(cuid())
benutzerId String? // null = für alle sichtbar (Team-Schlüssel)
name String
oeffentlich String
fingerabdruck String // SHA256:…
quelle String @default("lokal") // Kennung der SchluesselQuelle (später z. B. sshkeyvault)
@@unique([benutzerId, fingerabdruck])
}
model Server {
id String @id @default(cuid())
name String @unique // "rz1" – auch CLI-Name
url String // https://host:8006
tokenId String // user@realm!tokenid
tokenGeheimnis String // AES-256-GCM, v1:<iv>:<tag>:<daten>
tlsPruefen Boolean @default(true)
fingerabdruck String? // SHA-256, gepinnt bei selbst signiertem Zertifikat
sshAktiv Boolean @default(false)
sshBenutzer String @default("root")
sshPort Int @default(22)
sshPrivat String? // verschlüsselt
sshOeffentlich String?
sshHostSchluessel Json? // je Node gepinnt
templateVmidVon Int @default(9000)
templateVmidBis Int @default(9999)
gastVmidVon Int @default(100)
bauNetz Json? // Bridge, VLAN, IP/CIDR, Gateway für Template-Vorbereitung ohne SSH
standards Json? // Standard-Node, -Storage, -Bridge
faehigkeiten Json? // letzte Erkennung (Version, Storages, Rechte, Strategien)
erreichbar Boolean?
geprueftAm DateTime?
erstelltAm DateTime @default(now())
}
model Distribution {
id String @id // "debian-13"
name String
familie String
definition Json // validiertes YAML
dateiHash String
aktiv Boolean @default(true)
synchronisiertAm DateTime
}
model Profil {
id String @id // "docker"
name String
beschreibung String?
herkunft Herkunft
quelltext String // YAML wie eingegeben (Kommentare bleiben)
definition Json
aktiv Boolean @default(true)
geaendertVonId String?
geaendertAm DateTime @updatedAt
}
model Template {
id String @id @default(cuid())
serverId String
knoten String
vmid Int
distributionId String
storage String
imageQuelle String // URL + Prüfsumme des Images
gebautVonAuftragId String?
gebautAm DateTime
@@unique([serverId, vmid])
}
model Vorlage { // Preset
id String @id @default(cuid())
name String @unique // "docker-host-klein"
titel String
beschreibung String?
gastTyp GastTyp
parameter Json // Teilmenge der Auftrags-Anfrage
erstelltVonId String?
geaendertAm DateTime @updatedAt
}
model Auftrag {
id String @id @default(cuid())
kurzId String @unique // "d-7f3k2" für CLI und URLs
art AuftragsArt
status AuftragsStatus @default(WARTEND)
serverId String
knoten String?
gastTyp GastTyp?
vmid Int?
name String?
distributionId String?
profile String[]
anfrage Json // validierte Anfrage (Grundlage für „Wiederholen“)
plan Json // Plan zum Zeitpunkt des Starts
ergebnis Json? // IP, SSH-Befehl, Dauer
angelegt Json? // was zum Aufräumen gehört
fehler Json? // { phase, ursache, hinweis }
quelle Quelle
erstelltVonId String
wiederholtVonId String?
vorlageId String?
erstelltAm DateTime @default(now())
gestartetAm DateTime?
beendetAm DateTime?
phasen Phase[] log Logeintrag[]
@@index([serverId, erstelltAm]) @@index([status])
}
model Phase {
id String @id @default(cuid())
auftragId String
reihenfolge Int
schluessel String // "klonen"
titel String // "Klonen"
status PhasenStatus @default(OFFEN)
detail String?
ursache String?
hinweis String?
gestartetAm DateTime?
beendetAm DateTime?
@@unique([auftragId, schluessel])
}
model Logeintrag {
id BigInt @id @default(autoincrement()) // auch SSE-Event-ID
auftragId String
zeit DateTime @default(now())
stufe String // debug|info|warn|error
phase String?
nachricht String
daten Json?
@@index([auftragId, id])
}
model VmidReservierung {
serverId String
vmid Int
auftragId String
ablaufAm DateTime
@@id([serverId, vmid])
}
model Protokoll { // Audit-Log, nur anhängen
id BigInt @id @default(autoincrement())
zeit DateTime @default(now())
benutzerId String?
quelle Quelle
aktion String // "Server angelegt", "Ausrollung gestartet"
ziel String? // "server:rz1", "auftrag:d-7f3k2"
details Json?
ip String?
}
model Einstellung { schluessel String @id wert Json }
```
Datenbankregeln (CHECK/Trigger, wie in Laufzettel): `Protokoll` nur INSERT, VMID-Bereiche
`von < bis`, `Auftrag.vmid` innerhalb 100–999999999, ein Template je Server/VMID.
## 8. REST-API (`/api/v1`)
Authentifizierung: Sitzungs-Cookie (Web) **oder** `Authorization: Bearer pd_…` (CLI). Rolle und
Aktiv-Status werden pro Anfrage aus der DB gelesen. Jede schreibende Anfrage → `Protokoll`.
Fehler einheitlich `{ fehler: { code, nachricht, hinweis?, felder? } }`.
| Methode + Pfad | Rolle | Zweck |
|---|---|---|
| `POST /auth/anmelden`, `POST /auth/abmelden`, `GET /auth/oidc/start`, `GET /auth/oidc/rueckruf` | – | Anmeldung |
| `GET /ich` | alle | aktueller Benutzer, Rolle, Version |
| `GET /server` · `POST /server` · `GET/PATCH/DELETE /server/:name` | Admin schreibt | Server |
| `POST /server/pruefen` | Admin | Verbindungstest ohne Speichern (liefert Fingerprint zum Bestätigen) |
| `POST /server/:name/test` | alle | Version, Cluster/Nodes, Storages, Bridges, Rechte, Fähigkeiten |
| `GET /server/:name/ressourcen?knoten=` | alle | Nodes, freie Ressourcen, Storages, Bridges/VLANs/VNets, nächste freie ID |
| `POST /server/:name/ssh` | Admin | Schlüsselpaar erzeugen/erneuern, Host-Key pinnen |
| `GET /distributionen?typ=vm\|lxc` · `PATCH /distributionen/:id` | Admin schreibt | Katalog |
| `GET /profile` · `POST /profile` · `PUT/DELETE /profile/:id` · `POST /profile/pruefen` | Admin schreibt | Profile + Validierung |
| `GET /server/:name/templates` | alle | Templates je Node |
| `POST /server/:name/templates` | Admin, Operator | Template bauen → Auftrag |
| `POST /auftraege/plan` | alle | **Dry-Run**: Plan + CLI-Befehl, nichts wird angelegt |
| `POST /auftraege` | Operator+ | Ausrollen (VM/LXC) → `202 { id, kurzId }` |
| `POST /auftraege/stapel` | Operator+ | Batch (YAML/JSON-Liste) |
| `GET /auftraege?server=&typ=&distribution=&status=&seite=` | alle | Verlauf |
| `GET /auftraege/:id` | alle | Stand inkl. Phasen + Ergebnis |
| `GET /auftraege/:id/ereignisse` | alle | **SSE**: `phase`, `log`, `ende`; `Last-Event-ID` |
| `GET /auftraege/:id/log?ab=` | alle | Detail-Log |
| `POST /auftraege/:id/wiederholen` · `/abbrechen` · `/aufraeumen` | Operator+ | |
| `GET/POST/PATCH/DELETE /vorlagen…` | Operator+ | Presets |
| `GET/POST/DELETE /schluessel…` | eigene | SSH-Public-Keys (über `SchluesselQuelle`) |
| `GET/POST/DELETE /tokens…` | eigene | CLI-Tokens (Geheimnis nur einmal angezeigt) |
| `GET/POST/PATCH /benutzer…` | Admin | Benutzer, Rollen |
| `GET /protokoll?…` | Admin | Audit-Log |
| `GET/PUT /einstellungen` | Admin | Standardwerte, OIDC, Bereitstellungsschlüssel |
| `GET /openapi.json`, `GET /gesundheit` | – | Spezifikation, Healthcheck |
## 9. CLI (`cli/`, Go)
**Begründung Go:** ein statisches Binary pro Plattform (linux/darwin/windows × amd64/arm64, ~8 MB,
keine Laufzeit nötig – passt auf jeden Admin-Rechner und Jump-Host), sehr gute Unterstützung für
TTY-Erkennung, 24-bit-Farben und SSE. Der Nachteil (keine geteilten TS-Typen) wird durch den aus
`openapi.json` generierten Client (`oapi-codegen`) aufgefangen; ein CI-Schritt prüft, dass der
generierte Code aktuell ist. Bibliotheken: `cobra` (Befehle), `BurntSushi/toml`, `golang.org/x/term`.
- Konfig `~/.config/proxdeploy/config.toml` (`url`, `token`, optional `[profiles.<name>]` für mehrere
Instanzen), Rechte 0600; `PROXDEPLOY_URL`/`PROXDEPLOY_TOKEN` überschreiben.
- `login <url>` fragt Benutzername/Passwort ab, legt ein Token an und speichert es (oder `--token`).
- Ausgabe exakt nach Vorgabe; Renderer in `cli/internal/ausgabe/` mit Golden-File-Tests für Farbe,
`--no-color`, `--ascii`, `--quiet`, `--json`, `--dry-run`. Breite der Phasenspalte aus dem längsten
Phasennamen, Box-Breite aus dem Inhalt (Unicode-Breite beachtet).
- Exit-Codes: 0 Erfolg, 1 Auftrag fehlgeschlagen, 2 Bedienfehler, 3 Verbindung/Anmeldung.
- Die Web-GUI zeigt im letzten Assistentenschritt den passenden CLI-Befehl; erzeugt wird er vom
Server (`POST /auftraege/plan` liefert `cliBefehl`), damit beide Seiten dieselben Flags kennen.
## 10. Web-GUI
Seitenleiste mit Wortmarke, Server-Umschalter, Navigation (Übersicht, Ausrollen, Verlauf, Templates,
Vorlagen, Katalog, Server*, Einstellungen; * nur Admin schreibend). Panel-Karten mit 1-px-Linie,
Radius 14 px, Tokens aus der Farbtabelle der Vorgabe, dunkel zuerst, helles Theme umschaltbar
(`data-theme`), Kontrast ≥ 4,5:1 in beiden (wird in Tests geprüft). Statusanzeige als Komponente
`<Status>` mit Symbol + Farbe + Text.
- **Assistent** `/ausrollen`: Schritte als Route-Segmente mit dem Zustand in der URL (`?s=…`), damit
Zurückspringen, Neuladen und Teilen funktionieren; Vorbelegen per `?vorlage=` oder `?wiederholen=`.
Am Handy eine Schrittleiste oben, Formular einspaltig.
- **Live-Ansicht** `/auftraege/[kurzId]`: Terminal-Karte (JetBrains Mono) mit genau dem Aufbau der
CLI, darunter ausklappbares Detail-Log, am Ende die Ergebnis-Karte mit Kopieren-Knopf. Fallback auf
Polling, falls SSE durch den Proxy geblockt wird.
## 11. Sicherheit
- Secrets (Proxmox-Token, SSH-Private-Keys, OIDC-Client-Secret) mit AES-256-GCM, Schlüssel aus
`PROXDEPLOY_SCHLUESSEL` (32 Byte, Base64); Versionspräfix im Chiffrat für spätere Rotation.
Geheimnisse verlassen die API nie (nur „gesetzt“/Fingerprint).
- CLI-Tokens nur gehasht gespeichert; Passwörter argon2id; Anmeldung mit Rate-Limit.
- TLS zu Proxmox: Standard Systemvertrauen; bei selbst signiertem Zertifikat Pinning des
SHA-256-Fingerprints (TOFU **mit** Bestätigung im Formular). Kein globales `NODE_TLS_REJECT_UNAUTHORIZED`.
- Hinter Reverse-Proxy: `TRUST_PROXY=true` wertet `X-Forwarded-For/Proto` aus; Cookies `Secure`
abhängig vom tatsächlichen Schema (Lehre aus ProxLedger 0.11.1); SSE mit `X-Accel-Buffering: no`.
- Profil-Variablen werden für Shell gequotet; `run`-Schritte nur in Profilen, die ein Admin anlegt.
- Bereitstellungsschlüssel (für SSH in Gäste) wird nach der Provisionierung aus dem Gast entfernt.
## 12. Betrieb
- `docker-compose.yml`: `app`, `worker`, `postgres`, `migrate` (einmalig `prisma migrate deploy`).
Ein gemeinsames Image, Next `output: "standalone"`, Non-root.
- `deploy/install-lxc.sh`: auf dem Proxmox-Host ausführen → legt Debian-13-LXC an (per `pct`),
installiert Node, PostgreSQL, systemd-Units für app und worker, erzeugt `.env` mit Zufallsschlüssel.
- `.env.example` mit allen Variablen (`DATABASE_URL`, `PROXDEPLOY_SCHLUESSEL`, `SITZUNG_GEHEIMNIS`,
`APP_URL`, `TRUST_PROXY`, `OIDC_*`, `WORKER_PARALLEL`, `LOG_LEVEL`, `ADMIN_BENUTZER/PASSWORT` für
den ersten Start).
## 13. Dateistruktur
```
proxdeploy/
├─ catalog/{distros,profiles}/*.yaml
├─ cli/ Go-Modul: cmd/proxdeploy, internal/{api,ausgabe,konfig,befehle}
├─ src/
│ ├─ app/(app)/… Seiten (Übersicht, ausrollen, auftraege, server, templates, vorlagen, katalog, einstellungen)
│ ├─ app/(auth)/anmelden
│ ├─ app/api/v1/… Route Handler (dünn: Recht prüfen, zod, Service)
│ ├─ components/ UI-Bausteine (Karte, Status, Terminal, Assistent, Formulare)
│ ├─ engine/ siehe Abschnitt 6 – frameworkfrei, ohne Prisma-Import
│ ├─ server/services/ Regeln + Schreiboperationen, rufen Engine und Prisma
│ ├─ server/queries/ Lesemodelle
│ ├─ server/auth/ Sitzung, Token, OIDC, Rechte
│ ├─ worker/ main.ts, Senke (Ereignisse → DB + NOTIFY), Katalog-Sync
│ └─ lib/ kryptografie.ts, formate.ts, logger.ts
├─ prisma/schema.prisma, migrations/
├─ public/brand/ Logo-Assets, Favicons, Manifest-Icons
├─ assets/ Banner, Avatar (Repo)
├─ deploy/install-lxc.sh
├─ tests/ engine/*, api/*, e2e/*
├─ docs/architektur.md, docs/issues/
├─ CHANGELOG.md, DEVLOG.md, CLAUDE.md, README.md, .env.example
└─ docker-compose.yml, Dockerfile
```
## 14. Etappen und Meilensteine
| Version | Etappe | Issues | Test gegen echten Proxmox |
|---|---|---|---|
| v0.1.0 | 1 – Server-Verwaltung + Verbindungstest | #001–#003 | Verbindungstest, Fähigkeiten, Rechte |
| v0.2.0 | 2 – Katalog, Template-Bau, Debian-13-VM, Live-Fortschritt | #004–#008 | Template bauen, VM ausrollen, Fehler + Aufräumen |
| v0.3.0 | 3 – LXC, Ubuntu, Profile, Presets | #009–#012 | LXC mit/ohne SSH, docker/zabbix |
| v0.4.0 | 4 – CLI | #013 | alle Befehle gegen die laufende Instanz |
| v0.5.0 | 5 – Verlauf, Audit, Rollen, OIDC | #014–#016 | – |
| v0.6.0 | 6 – Branding, README, Installer | #017–#019 | Installer in echtem LXC |
| v1.0.0 | Feldtest 8.x + 9.x, Feinschliff | #020 | vollständig |
Bis die Testumgebung steht, entwickle ich gegen einen **Proxmox-Mock** (`tests/proxmox-mock/`, ein
kleiner HTTP-Server mit Zustand, der die benutzten Endpunkte inkl. UPID-Tasks nachbildet). Die
Abnahme jeder Etappe gegen echten Proxmox wird nachgeholt und im DEVLOG vermerkt; ohne sie wird die
Version nicht als abgeschlossen markiert.
## 15. Offene Punkte
1. **Ubuntu 26.04** zusätzlich mitliefern? (Vorschlag: ja)
2. **Standard-BIOS** für Templates: OVMF (UEFI, braucht EFI-Disk) oder SeaBIOS? (Vorschlag: OVMF,
pro Distribution überschreibbar)
3. **Template-VMID-Schema**: feste Zuordnung je Distribution (Debian 13 → 9013, Ubuntu 24.04 → 9024)
plus Node-Versatz im Cluster mit lokalem Storage (pve02 → 9113)? (Vorschlag: ja)
4. **Domain** für `APP_URL` und OIDC-Redirect – kann später in `.env` gesetzt werden.
+27
View File
@@ -0,0 +1,27 @@
# #001 Projekt-Setup
**Status:** offen
**Meilenstein:** v0.1.0
**Labels:** chore, infrastruktur
**Geschätzt:** 4 h **Tatsächlich:** –
## Ziel
Lauffähiges Grundgerüst mit Projektorganisation, Design-Tokens und Prüfwerkzeugen.
## Aufgaben
- [ ] Git, `.gitignore`, CHANGELOG, DEVLOG, Issues, `docs/architektur.md`, `CLAUDE.md` mit Namensabbildung
- [ ] Next.js 16, TypeScript strict, Tailwind 4 mit Tokens aus der Farbtabelle (dunkel + hell)
- [ ] Prisma 7 + Docker-Compose für Entwicklung (Postgres), Vitest mit Test-DB, ESLint/Prettier
- [ ] Versionen exakt pinnen und begründen; Build/Tests laufen in Docker (lokal kein Node)
- [ ] Grundlayout: Seitenleiste mit Wortmarke, Server-Umschalter, Theme-Umschalter, `<Status>`-Komponente
- [ ] Lokale Anmeldung (erster Admin aus `.env`), Sitzungs-Cookie, `/api/v1/gesundheit`
- [ ] pino-Logger (JSON), `.env.example`
## Akzeptanzkriterien
- [ ] `lint`, `typecheck`, `test`, `build` grün im Container
- [ ] Anmeldung funktioniert im gebauten Stand; keine Secrets im Repo
## Abhängigkeiten
–
## Notizen / Entscheidungen
–
@@ -0,0 +1,26 @@
# #002 Server-Verwaltung und Verschlüsselung
**Status:** offen
**Meilenstein:** v0.1.0
**Labels:** feature, sicherheit
**Geschätzt:** 6 h **Tatsächlich:** –
## Ziel
Proxmox-Server anlegen, bearbeiten, löschen; Geheimnisse verschlüsselt.
## Aufgaben
- [ ] AES-256-GCM-Helfer mit Versionspräfix, Tests (falscher Schlüssel, manipulierte Daten)
- [ ] Modell `Server`, Services, REST `/api/v1/server`, Formular mit Token-ID/Secret
- [ ] TLS: Fingerprint holen (`POST /server/pruefen`), anzeigen, bestätigen, pinnen
- [ ] Proxmox-Client mit undici-Agent und Fingerprint-Vergleich, UPID-Warten
- [ ] SSH optional: Schlüsselpaar erzeugen, Einzeiler anzeigen, Host-Key pinnen
- [ ] Protokolleintrag je Änderung
## Akzeptanzkriterien
- [ ] Secret erscheint nie in API-Antworten oder Logs
- [ ] Falscher Fingerprint → klare Fehlermeldung, keine Verbindung
## Abhängigkeiten
#001
## Notizen / Entscheidungen
–
+25
View File
@@ -0,0 +1,25 @@
# #003 Verbindungstest und Fähigkeitserkennung
**Status:** offen
**Meilenstein:** v0.1.0
**Labels:** feature, proxmox
**Geschätzt:** 6 h **Tatsächlich:** –
## Ziel
Button „Verbindung testen“ zeigt Version, Cluster/Nodes, Storages, Bridges/VLANs und was per API bzw. nur per SSH geht.
## Aufgaben
- [ ] `/version`, `/cluster/status`, `/cluster/resources`, `/nodes/{n}/storage`, `/nodes/{n}/network`, SDN-VNets
- [ ] Rechte über `/access/permissions` gegen Rolle (8.x/9.x-Variante) abgleichen, fehlende einzeln nennen
- [ ] Fähigkeiten: download-url import (≥ 8.4), import-from per Volume, Snippets-Storage, SSH erreichbar
- [ ] Übersichtsseite mit Serverstatus und freien Ressourcen; Serverdetail mit Nodes/Storages/Bridges
- [ ] Proxmox-Mock für Tests (8.3, 8.4, 9.2 als Varianten)
## Akzeptanzkriterien
- [ ] Gegen echten Proxmox geprüft (sobald Testumgebung da ist)
- [ ] Tests decken 8.3 (SSH nötig), 8.4 und 9.2 ab
## Abhängigkeiten
#002
## Notizen / Entscheidungen
–
+24
View File
@@ -0,0 +1,24 @@
# #004 Katalog: Distributionen und Profile als Daten
**Status:** offen
**Meilenstein:** v0.2.0
**Labels:** feature, katalog
**Geschätzt:** 5 h **Tatsächlich:** –
## Ziel
YAML-Katalog laden, validieren, in die DB synchronisieren, in der GUI ansehen und deaktivieren.
## Aufgaben
- [ ] zod-Schemas für `catalog/distros` und `catalog/profiles`
- [ ] Debian 12/13, Ubuntu 22.04/24.04 (URLs/Prüfsummen geprüft am 2026-10-12)
- [ ] Prüfsummen-Parser für beide Zeilenformate; LXC-Musterauflösung mit Versionssortierung
- [ ] Sync beim worker-Start (Upsert, Aktiv-Schalter bleibt), fehlerhafte Datei überspringen + melden
- [ ] Katalog-Seite (Distributionen, Profile, aktiv/inaktiv)
## Akzeptanzkriterien
- [ ] Unit-Tests für Schema, Sync und Muster
## Abhängigkeiten
#001
## Notizen / Entscheidungen
–
+23
View File
@@ -0,0 +1,23 @@
# #005 Adapter debian und Profil-Engine
**Status:** offen
**Meilenstein:** v0.2.0
**Labels:** feature, engine
**Geschätzt:** 6 h **Tatsächlich:** –
## Ziel
Interface `FamilienAdapter`, Umsetzung `debian`, Profil `base` wird zu einem idempotenten Skript.
## Aufgaben
- [ ] Interface + Registry; `debian`-Adapter (apt, systemd, deb822, locale, keyboard, sshd_config.d)
- [ ] Profilauflösung (requires, Reihenfolge, Variablen, Shell-Quoting)
- [ ] Profil `base` (Updates, Guest-Agent, Europe/Berlin, de_DE.UTF-8, Tastatur de, unattended-upgrades, sudo-User, SSH-Härtung, chrony, fail2ban optional)
- [ ] Snapshot-Tests der erzeugten Skripte; shellcheck auf die Ausgabe
## Akzeptanzkriterien
- [ ] Neues Profil/neuer Adapter braucht keine Änderung an Engine-Code
## Abhängigkeiten
#004
## Notizen / Entscheidungen
–
+25
View File
@@ -0,0 +1,25 @@
# #006 Aufträge, Phasen, worker und Live-Fortschritt
**Status:** offen
**Meilenstein:** v0.2.0
**Labels:** feature, engine
**Geschätzt:** 8 h **Tatsächlich:** –
## Ziel
Hintergrund-Jobs mit Phasen in der DB, live per SSE in der Web-GUI.
## Aufgaben
- [ ] pg-boss im worker, Modelle `Auftrag`, `Phase`, `Logeintrag`, `VmidReservierung`
- [ ] Phasen-Runner mit Ereignis-Senke (DB + NOTIFY), Fehler mit Ursache + Hinweis
- [ ] SSE-Endpunkt mit Last-Event-ID, Polling-Fallback
- [ ] VMID-Vergabe mit Advisory-Lock, Test mit parallelen Aufträgen
- [ ] Live-Ansicht als Terminal-Karte, Detail-Log, Ergebnis-Karte
## Akzeptanzkriterien
- [ ] Zwei parallele Aufträge bekommen nie dieselbe VMID (Test)
- [ ] Neuladen der Seite mitten im Auftrag zeigt den vollständigen Stand
## Abhängigkeiten
#003
## Notizen / Entscheidungen
–
+25
View File
@@ -0,0 +1,25 @@
# #007 Template-Bau für VMs
**Status:** offen
**Meilenstein:** v0.2.0
**Labels:** feature, proxmox
**Geschätzt:** 8 h **Tatsächlich:** –
## Ziel
Cloud-Image laden, Prüfsumme prüfen, VM anlegen, Disk importieren, Guest-Agent einbauen, als Template markieren.
## Aufgaben
- [ ] Weg A (≥ 8.4): `download-url` (content=import, checksum) + `import-from` per Volume-ID
- [ ] Weg B (SSH): Download + Prüfung auf dem Node, Import per `qm importdisk`/`import-from`
- [ ] Hardware: q35, OVMF+EFI-Disk oder SeaBIOS, virtio-scsi-single, discard/ssd/iothread, CPU host, serielle Konsole, agent=1, cloud-init-Laufwerk
- [ ] Guest-Agent einbauen: Snippet (SSH) oder Bau-Adresse + SSH in die VM
- [ ] Template-Übersicht je Server/Node, VMID-Bereich konfigurierbar
- [ ] Singleton je Server+Distribution
## Akzeptanzkriterien
- [ ] Debian-13-Template auf echtem Proxmox gebaut (8.x und 9.x, sobald verfügbar)
## Abhängigkeiten
#005, #006
## Notizen / Entscheidungen
–
+24
View File
@@ -0,0 +1,24 @@
# #008 Deploy-Assistent und VM ausrollen
**Status:** offen
**Meilenstein:** v0.2.0
**Labels:** feature
**Geschätzt:** 10 h **Tatsächlich:** –
## Ziel
Debian-13-VM aus Template vollständig unattended, Assistent mit Dry-Run und CLI-Befehl.
## Aufgaben
- [ ] Planer VM (Phasen + geplante API-Aufrufe), `POST /auftraege/plan`
- [ ] Ausführung: Klonen, Disk vergrößern, cloud-init (sshkeys korrekt kodiert, ciupgrade=0), Start, Agent, Provisionierung per Agent
- [ ] Fehlendes Template → automatisch bauen anbieten
- [ ] Assistent mit 9 Schritten, URL-Zustand, Handy-tauglich
- [ ] Aufräumen nach Bestätigung
## Akzeptanzkriterien
- [ ] VM mit base-Profil per SSH erreichbar; Fehlerfall zeigt Ursache + Hinweis
## Abhängigkeiten
#007
## Notizen / Entscheidungen
–
+21
View File
@@ -0,0 +1,21 @@
# #009 LXC-Container ausrollen
**Status:** offen
**Meilenstein:** v0.3.0
**Labels:** feature, proxmox
**Geschätzt:** 8 h **Tatsächlich:** –
## Ziel
LXC aus Proxmox-Template, Provisionierung per pct exec (SSH zum Node) oder SSH in den Container.
## Aufgaben
- [ ] aplinfo prüfen/herunterladen, Planer LXC, Swap, unprivilegiert, Features (nesting per API, keyctl nur per SSH)
- [ ] Transport `ssh-node` (pct exec) und `ssh-gast` (Bereitstellungsschlüssel, danach entfernen)
## Akzeptanzkriterien
- [ ] LXC mit base auf echtem Proxmox, einmal mit und einmal ohne Node-SSH
## Abhängigkeiten
#008
## Notizen / Entscheidungen
–
+21
View File
@@ -0,0 +1,21 @@
# #010 Ubuntu 22.04 und 24.04
**Status:** offen
**Meilenstein:** v0.3.0
**Labels:** feature, katalog
**Geschätzt:** 3 h **Tatsächlich:** –
## Ziel
Ubuntu als VM und LXC über den debian-Adapter.
## Aufgaben
- [ ] Unterschiede im Adapter (Codename-Ermittlung, Locale-Paket, unattended-upgrades)
- [ ] ggf. Ubuntu 26.04 (offener Punkt)
## Akzeptanzkriterien
- [ ] Je eine Ubuntu-VM und ein Ubuntu-LXC ausgerollt
## Abhängigkeiten
#009
## Notizen / Entscheidungen
–
+22
View File
@@ -0,0 +1,22 @@
# #011 Profile docker und zabbix-agent, Variablen, Editor
**Status:** offen
**Meilenstein:** v0.3.0
**Labels:** feature
**Geschätzt:** 6 h **Tatsächlich:** –
## Ziel
Kombinierbare Profile mit Variablen; eigene Profile im YAML-Editor.
## Aufgaben
- [ ] docker (deb822, nesting bei LXC automatisch), zabbix-agent (7.0 LTS, Server/ServerActive)
- [ ] Variablen als Formularfelder; Ausgrauen ohne Umsetzung für die Familie
- [ ] YAML-Editor mit Validierung und Fehlerstelle
## Akzeptanzkriterien
- [ ] docker run hello-world im ausgerollten Gast erfolgreich
## Abhängigkeiten
#005
## Notizen / Entscheidungen
–
+20
View File
@@ -0,0 +1,20 @@
# #012 Vorlagen (Presets)
**Status:** offen
**Meilenstein:** v0.3.0
**Labels:** feature
**Geschätzt:** 3 h **Tatsächlich:** –
## Ziel
Gespeicherte Kombinationen, im Assistenten mit einem Klick vorbelegbar.
## Aufgaben
- [ ] CRUD, „aus diesem Auftrag als Vorlage speichern“, Beispiel „Docker-Host klein“
## Akzeptanzkriterien
- [ ] `--preset` in API und Assistent identisch aufgelöst
## Abhängigkeiten
#008
## Notizen / Entscheidungen
–
+23
View File
@@ -0,0 +1,23 @@
# #013 CLI proxdeploy (Go)
**Status:** offen
**Meilenstein:** v0.4.0
**Labels:** feature, cli
**Geschätzt:** 12 h **Tatsächlich:** –
## Ziel
Alle Befehle aus der Vorgabe, Ausgabe exakt im Format der Vorgabe.
## Aufgaben
- [ ] OpenAPI aus zod, Go-Client per oapi-codegen, Prüfung auf Aktualität
- [ ] login, servers, distros, profiles, vm, lxc, template build, batch, status, logs --follow
- [ ] Renderer: Farbe 24-bit/256/aus, --ascii, --quiet, --json, --dry-run; Golden-Tests
- [ ] Release-Builds für linux/darwin/windows × amd64/arm64
## Akzeptanzkriterien
- [ ] Ausgabe entspricht zeichengenau dem Mockup (Golden-File)
## Abhängigkeiten
#008, #012
## Notizen / Entscheidungen
–
+21
View File
@@ -0,0 +1,21 @@
# #014 Verlauf und Wiederholen
**Status:** offen
**Meilenstein:** v0.5.0
**Labels:** feature
**Geschätzt:** 4 h **Tatsächlich:** –
## Ziel
Alle Aufträge filterbar, mit gleichen Parametern wiederholen.
## Aufgaben
- [ ] Filter Server/Typ/Distribution/Status, Seitenblättern
- [ ] Wiederholen (neue VMID/Name abfragen)
## Akzeptanzkriterien
- [ ] Filter bleiben in der URL
## Abhängigkeiten
#008
## Notizen / Entscheidungen
–
+22
View File
@@ -0,0 +1,22 @@
# #015 Rollen, Benutzer, Tokens, Audit-Log
**Status:** offen
**Meilenstein:** v0.5.0
**Labels:** feature, sicherheit
**Geschätzt:** 6 h **Tatsächlich:** –
## Ziel
Admin/Operator serverseitig erzwungen, jede Aktion protokolliert.
## Aufgaben
- [ ] Rechte-Tabelle, strukturelle Prüfung aller Route Handler (Test)
- [ ] Benutzerverwaltung, CLI-Tokens, SSH-Keys über `SchluesselQuelle`
- [ ] Audit-Log-Ansicht mit Filter
## Akzeptanzkriterien
- [ ] Operator kann keine Server/Katalog/Benutzer ändern (Tests)
## Abhängigkeiten
#001
## Notizen / Entscheidungen
–
+20
View File
@@ -0,0 +1,20 @@
# #016 Anmeldung per OIDC
**Status:** offen
**Meilenstein:** v0.5.0
**Labels:** feature, sicherheit
**Geschätzt:** 4 h **Tatsächlich:** –
## Ziel
Optionale OIDC-Anmeldung (z. B. authentik), Rollen über Gruppen-Claim.
## Aufgaben
- [ ] openid-client mit PKCE, Discovery, Gruppen → Rolle, Konto-Verknüpfung
## Akzeptanzkriterien
- [ ] Tests gegen einen Mock-IdP (echter IdP später)
## Abhängigkeiten
#015
## Notizen / Entscheidungen
–
+21
View File
@@ -0,0 +1,21 @@
# #017 Branding-Assets
**Status:** offen
**Meilenstein:** v0.6.0
**Labels:** design
**Geschätzt:** 4 h **Tatsächlich:** –
## Ziel
Alle Logo-Varianten, Banner, Favicons, Manifest-Icons nach Vorgabe.
## Aufgaben
- [ ] symbol(-light), icon-small, avatar, logo(-light), banner 1280×640
- [ ] Favicon 16/32, Apple-Touch-Icon, Manifest-Icons per Skript aus SVG
## Akzeptanzkriterien
- [ ] Darstellung in beiden Themes geprüft
## Abhängigkeiten
#001
## Notizen / Entscheidungen
–
+21
View File
@@ -0,0 +1,21 @@
# #018 README und Dokumentation
**Status:** offen
**Meilenstein:** v0.6.0
**Labels:** doku
**Geschätzt:** 4 h **Tatsächlich:** –
## Ziel
README mit Banner, Screenshots, Installation, CLI, Proxmox-Rechten, neuer Distribution, eigenem Profil.
## Aufgaben
- [ ] Screenshots aus dem gebauten Stand
- [ ] Hinweis: unabhängiges Tool, nicht von Proxmox Server Solutions
## Akzeptanzkriterien
- [ ] Anleitungen einmal Schritt für Schritt nachvollzogen
## Abhängigkeiten
#013
## Notizen / Entscheidungen
–
+21
View File
@@ -0,0 +1,21 @@
# #019 Docker-Image und LXC-Installer
**Status:** offen
**Meilenstein:** v0.6.0
**Labels:** infrastruktur
**Geschätzt:** 6 h **Tatsächlich:** –
## Ziel
Produktionsbetrieb per Docker Compose oder in einem Debian-LXC.
## Aufgaben
- [ ] Dockerfile (standalone, non-root), Compose mit migrate
- [ ] `deploy/install-lxc.sh` (auf dem Proxmox-Host), Update-Pfad
## Akzeptanzkriterien
- [ ] Installer in echtem LXC durchgelaufen
## Abhängigkeiten
#001
## Notizen / Entscheidungen
–
+21
View File
@@ -0,0 +1,21 @@
# #020 Feldtest 8.x und 9.x, Version 1.0.0
**Status:** offen
**Meilenstein:** v1.0.0
**Labels:** test
**Geschätzt:** 6 h **Tatsächlich:** –
## Ziel
Vollständiger Durchlauf gegen echte Proxmox-Hosts beider Hauptversionen.
## Aufgaben
- [ ] Matrix: VM/LXC × Debian/Ubuntu × mit/ohne SSH × 8.x/9.x
- [ ] Gefundene Fehler als eigene Issues
## Akzeptanzkriterien
- [ ] Alle Kombinationen grün oder mit Begründung ausgeschlossen
## Abhängigkeiten
alle
## Notizen / Entscheidungen
–
+26
View File
@@ -0,0 +1,26 @@
# Issue-Übersicht
Details und Architektur: [../architektur.md](../architektur.md). Zeiten werden aus dem [DEVLOG](../../DEVLOG.md) übernommen.
| Nr. | Titel | Status | Meilenstein | Geschätzt | Tatsächlich |
|---|---|---|---|---|---|
| [#001](001-projekt-setup.md) | Projekt-Setup | offen | v0.1.0 | 4 h | – |
| [#002](002-verschluesselung-server.md) | Server-Verwaltung und Verschlüsselung | offen | v0.1.0 | 6 h | – |
| [#003](003-verbindungstest.md) | Verbindungstest und Fähigkeitserkennung | offen | v0.1.0 | 6 h | – |
| [#004](004-katalog.md) | Katalog: Distributionen und Profile als Daten | offen | v0.2.0 | 5 h | – |
| [#005](005-adapter-profile.md) | Adapter debian und Profil-Engine | offen | v0.2.0 | 6 h | – |
| [#006](006-auftraege-sse.md) | Aufträge, Phasen, worker und Live-Fortschritt | offen | v0.2.0 | 8 h | – |
| [#007](007-template-bau.md) | Template-Bau für VMs | offen | v0.2.0 | 8 h | – |
| [#008](008-vm-ausrollen.md) | Deploy-Assistent und VM ausrollen | offen | v0.2.0 | 10 h | – |
| [#009](009-lxc.md) | LXC-Container ausrollen | offen | v0.3.0 | 8 h | – |
| [#010](010-ubuntu.md) | Ubuntu 22.04 und 24.04 | offen | v0.3.0 | 3 h | – |
| [#011](011-profile-docker-zabbix.md) | Profile docker und zabbix-agent, Variablen, Editor | offen | v0.3.0 | 6 h | – |
| [#012](012-vorlagen.md) | Vorlagen (Presets) | offen | v0.3.0 | 3 h | – |
| [#013](013-cli.md) | CLI proxdeploy (Go) | offen | v0.4.0 | 12 h | – |
| [#014](014-verlauf.md) | Verlauf und Wiederholen | offen | v0.5.0 | 4 h | – |
| [#015](015-rollen-audit.md) | Rollen, Benutzer, Tokens, Audit-Log | offen | v0.5.0 | 6 h | – |
| [#016](016-oidc.md) | Anmeldung per OIDC | offen | v0.5.0 | 4 h | – |
| [#017](017-branding.md) | Branding-Assets | offen | v0.6.0 | 4 h | – |
| [#018](018-readme.md) | README und Dokumentation | offen | v0.6.0 | 4 h | – |
| [#019](019-installer.md) | Docker-Image und LXC-Installer | offen | v0.6.0 | 6 h | – |
| [#020](020-feldtest.md) | Feldtest 8.x und 9.x, Version 1.0.0 | offen | v1.0.0 | 6 h | – |