From 6c16d5696094be68515151e4d9fccff14fef53c2 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Mon, 12 Oct 2026 09:05:57 +0200 Subject: [PATCH] docs: Architekturplan, Issues und Projektorganisation anlegen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .gitignore | 15 + CHANGELOG.md | 20 + DEVLOG.md | 37 ++ docs/architektur.md | 602 +++++++++++++++++++++ docs/issues/001-projekt-setup.md | 27 + docs/issues/002-verschluesselung-server.md | 26 + docs/issues/003-verbindungstest.md | 25 + docs/issues/004-katalog.md | 24 + docs/issues/005-adapter-profile.md | 23 + docs/issues/006-auftraege-sse.md | 25 + docs/issues/007-template-bau.md | 25 + docs/issues/008-vm-ausrollen.md | 24 + docs/issues/009-lxc.md | 21 + docs/issues/010-ubuntu.md | 21 + docs/issues/011-profile-docker-zabbix.md | 22 + docs/issues/012-vorlagen.md | 20 + docs/issues/013-cli.md | 23 + docs/issues/014-verlauf.md | 21 + docs/issues/015-rollen-audit.md | 22 + docs/issues/016-oidc.md | 20 + docs/issues/017-branding.md | 21 + docs/issues/018-readme.md | 21 + docs/issues/019-installer.md | 21 + docs/issues/020-feldtest.md | 21 + docs/issues/README.md | 26 + 25 files changed, 1153 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 DEVLOG.md create mode 100644 docs/architektur.md create mode 100644 docs/issues/001-projekt-setup.md create mode 100644 docs/issues/002-verschluesselung-server.md create mode 100644 docs/issues/003-verbindungstest.md create mode 100644 docs/issues/004-katalog.md create mode 100644 docs/issues/005-adapter-profile.md create mode 100644 docs/issues/006-auftraege-sse.md create mode 100644 docs/issues/007-template-bau.md create mode 100644 docs/issues/008-vm-ausrollen.md create mode 100644 docs/issues/009-lxc.md create mode 100644 docs/issues/010-ubuntu.md create mode 100644 docs/issues/011-profile-docker-zabbix.md create mode 100644 docs/issues/012-vorlagen.md create mode 100644 docs/issues/013-cli.md create mode 100644 docs/issues/014-verlauf.md create mode 100644 docs/issues/015-rollen-audit.md create mode 100644 docs/issues/016-oidc.md create mode 100644 docs/issues/017-branding.md create mode 100644 docs/issues/018-readme.md create mode 100644 docs/issues/019-installer.md create mode 100644 docs/issues/020-feldtest.md create mode 100644 docs/issues/README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bd802b --- /dev/null +++ b/.gitignore @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5d79e36 --- /dev/null +++ b/CHANGELOG.md @@ -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 diff --git a/DEVLOG.md b/DEVLOG.md new file mode 100644 index 0000000..c41a3cd --- /dev/null +++ b/DEVLOG.md @@ -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). diff --git a/docs/architektur.md b/docs/architektur.md new file mode 100644 index 0000000..8dfedf0 --- /dev/null +++ b/docs/architektur.md @@ -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_` 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: :0,import-from=:import/`) | 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=` (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/` (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/`, `/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//latest/debian--genericcloud-amd64.qcow2`, + `SHA512SUMS` (Zeilenformat `␣␣`), Benutzer `debian` +- Ubuntu: `cloud-images.ubuntu.com//current/-server-cloudimg-amd64.img` (qcow2), + `SHA256SUMS` (Zeilenformat `␣*`), 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.` 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::: + 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.]` für mehrere + Instanzen), Rechte 0600; `PROXDEPLOY_URL`/`PROXDEPLOY_TOKEN` überschreiben. +- `login ` 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 +`` 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. diff --git a/docs/issues/001-projekt-setup.md b/docs/issues/001-projekt-setup.md new file mode 100644 index 0000000..1912c2b --- /dev/null +++ b/docs/issues/001-projekt-setup.md @@ -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, ``-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 +– diff --git a/docs/issues/002-verschluesselung-server.md b/docs/issues/002-verschluesselung-server.md new file mode 100644 index 0000000..4e29817 --- /dev/null +++ b/docs/issues/002-verschluesselung-server.md @@ -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 +– diff --git a/docs/issues/003-verbindungstest.md b/docs/issues/003-verbindungstest.md new file mode 100644 index 0000000..3dcc960 --- /dev/null +++ b/docs/issues/003-verbindungstest.md @@ -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 +– diff --git a/docs/issues/004-katalog.md b/docs/issues/004-katalog.md new file mode 100644 index 0000000..0fc61d5 --- /dev/null +++ b/docs/issues/004-katalog.md @@ -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 +– diff --git a/docs/issues/005-adapter-profile.md b/docs/issues/005-adapter-profile.md new file mode 100644 index 0000000..0a03805 --- /dev/null +++ b/docs/issues/005-adapter-profile.md @@ -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 +– diff --git a/docs/issues/006-auftraege-sse.md b/docs/issues/006-auftraege-sse.md new file mode 100644 index 0000000..6c09f63 --- /dev/null +++ b/docs/issues/006-auftraege-sse.md @@ -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 +– diff --git a/docs/issues/007-template-bau.md b/docs/issues/007-template-bau.md new file mode 100644 index 0000000..5790e45 --- /dev/null +++ b/docs/issues/007-template-bau.md @@ -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 +– diff --git a/docs/issues/008-vm-ausrollen.md b/docs/issues/008-vm-ausrollen.md new file mode 100644 index 0000000..3d50087 --- /dev/null +++ b/docs/issues/008-vm-ausrollen.md @@ -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 +– diff --git a/docs/issues/009-lxc.md b/docs/issues/009-lxc.md new file mode 100644 index 0000000..cb9a706 --- /dev/null +++ b/docs/issues/009-lxc.md @@ -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 +– diff --git a/docs/issues/010-ubuntu.md b/docs/issues/010-ubuntu.md new file mode 100644 index 0000000..a59944d --- /dev/null +++ b/docs/issues/010-ubuntu.md @@ -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 +– diff --git a/docs/issues/011-profile-docker-zabbix.md b/docs/issues/011-profile-docker-zabbix.md new file mode 100644 index 0000000..4ca32f4 --- /dev/null +++ b/docs/issues/011-profile-docker-zabbix.md @@ -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 +– diff --git a/docs/issues/012-vorlagen.md b/docs/issues/012-vorlagen.md new file mode 100644 index 0000000..c9a428a --- /dev/null +++ b/docs/issues/012-vorlagen.md @@ -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 +– diff --git a/docs/issues/013-cli.md b/docs/issues/013-cli.md new file mode 100644 index 0000000..f321259 --- /dev/null +++ b/docs/issues/013-cli.md @@ -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 +– diff --git a/docs/issues/014-verlauf.md b/docs/issues/014-verlauf.md new file mode 100644 index 0000000..69bcd88 --- /dev/null +++ b/docs/issues/014-verlauf.md @@ -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 +– diff --git a/docs/issues/015-rollen-audit.md b/docs/issues/015-rollen-audit.md new file mode 100644 index 0000000..651bec2 --- /dev/null +++ b/docs/issues/015-rollen-audit.md @@ -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 +– diff --git a/docs/issues/016-oidc.md b/docs/issues/016-oidc.md new file mode 100644 index 0000000..1197db5 --- /dev/null +++ b/docs/issues/016-oidc.md @@ -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 +– diff --git a/docs/issues/017-branding.md b/docs/issues/017-branding.md new file mode 100644 index 0000000..57e2ae4 --- /dev/null +++ b/docs/issues/017-branding.md @@ -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 +– diff --git a/docs/issues/018-readme.md b/docs/issues/018-readme.md new file mode 100644 index 0000000..54cc162 --- /dev/null +++ b/docs/issues/018-readme.md @@ -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 +– diff --git a/docs/issues/019-installer.md b/docs/issues/019-installer.md new file mode 100644 index 0000000..e61064d --- /dev/null +++ b/docs/issues/019-installer.md @@ -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 +– diff --git a/docs/issues/020-feldtest.md b/docs/issues/020-feldtest.md new file mode 100644 index 0000000..64fe95c --- /dev/null +++ b/docs/issues/020-feldtest.md @@ -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 +– diff --git a/docs/issues/README.md b/docs/issues/README.md new file mode 100644 index 0000000..3334864 --- /dev/null +++ b/docs/issues/README.md @@ -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 | – |