Commit Graph
71 Commits
Author SHA1 Message Date
adminandClaude Sonnet 5 ed217032d8 Umlaut-Fix Kategorie + fedora-Backend vollständig entfernt
- Migration 0041: "Buero & Produktivitaet" -> "Büro & Produktivität"
  (ASCII-Ersatzschreibweise, key unveraendert - Fremdschluessel laufen
  ueber kategorie_id, nicht ueber key).
- Migration 0042: fedora-Backend nach Nutzer-Bestaetigung komplett
  entfernt (siehe Rueckfrage bei Migration 0040) - dazu erst die eine
  darauf verweisende Bereitstellungsvorlage ("Schulcomputer (Fedora)")
  und ihre 4 historischen Zuweisungen an bereits archivierte Testgeraete
  geloescht (sonst ON DELETE RESTRICT), dann das Backend selbst
  (Blueprints kaskadieren automatisch mit).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 16:14:50 +02:00
adminandClaude Sonnet 5 af6af03b43 Zwei echte Anzeige-Bugs gefixt, Backend-Namen bereinigt
- debug_mode_until wurde beim Ablaufen nie geloescht - Auftragskatalog-
  Seite und Geraeteliste zeigten den Debug-Modus dadurch dauerhaft als
  aktiv an, auch Tage nach Ablauf (live an zwei echten Geraeten gefunden,
  abgelaufen am 02./09.09., trotzdem noch "aktiv" angezeigt).
  _health_felder_aus_row() liefert das Feld jetzt nur noch, wenn es
  tatsaechlich noch in der Zukunft liegt.
- fetch_unassigned_devices() (Flows "Unbestaetigte Geraete") hatte den
  archived_at-Filter nie bekommen, den fetch_devices_for_organization()/
  fetch_all_devices() schon hatten - bereits geloeschte Geraete ohne
  Zuweisung erschienen dadurch dauerhaft als offene Aufgabe.
- Migration 0040: Backend-Namen gekuerzt (Debian/Mint/Ubuntu statt
  "... (Golden Image) ..."), altes "mint"-Backend (vor dem Golden-Image-
  Pivot, keine Bereitstellungsvorlage nutzt es mehr) geloescht. "fedora"
  bewusst NICHT angefasst - echte Bereitstellungsvorlage+Zuweisungen
  haengen dran, siehe Rueckfrage an den Nutzer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 15:41:14 +02:00
adminandClaude Sonnet 5 f337718ec6 Geräteliste: Workspace-Name mitliefern (GUI-Umbau)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 14:51:54 +02:00
adminandClaude Sonnet 5 bfbd8f7b17 Hostnamenkonvention: serverseitige Vergabe für neue Geräte
- Migration 0039: organizations.hostname_praefix/-stellen/-nummer_modus/
  -naechste_sequenz. Leerer Präfix = keine Konvention (unverändertes
  Verhalten, Client bestimmt den Hostnamen selbst).
- generate_hostname_for_organization(): erzeugt "<praefix>-<nummer>",
  sequenziell (Zeilen-Lock via FOR UPDATE) oder zufällig (freie Nummer
  per secrets.randbelow(), Kollisionsprüfung gegen vorhandene Hostnamen,
  Fallback auf nächste freie sequenzielle Nummer nach 20 Versuchen).
- resolve_hostname_for_activation(): greift nur bei einem GENUIN NEUEN
  Gerät (device_fingerprint noch unbekannt) - eine Re-Aktivierung eines
  bekannten Geräts behält immer den vom Client gemeldeten Hostnamen,
  überschreibt also keine manuelle Umbenennung.
- agent_checkin(): liefert jetzt auch "hostname" (devices.hostname) an
  den Agenten.
- Neue Endpunkte GET/PUT /api/v1/organizations/{id}/hostname-konvention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 12:06:35 +02:00
adminandClaude Sonnet 5 10d31f8cf3 Gerätegruppen mit alternativem Workspace, device_merkmale abgelöst
- Migration 0038: gruppen.workspace_id (nullable FK), device_merkmale
  Tabelle entfernt (keine Datenzeilen auf anode, geprüft vor Drop).
- set_gruppe_workspace()/clear_gruppe_workspace(): weist einer
  Gerätegruppe einen alternativen Workspace zu, ersetzt die komplette
  Baseline für Mitgliedsgeräte statt nur einzelner Merkmale. Prüft
  Organisationszugehörigkeit des Workspace (global oder eigene Org) und
  die Exklusivitätsregel (ein Gerät nur in einer workspace-tragenden
  Gruppe gleichzeitig) - sowohl beim Zuweisen als auch beim Hinzufügen
  eines Geräts zu einer bereits workspace-tragenden Gruppe.
- resolve_effective_workspace_id(): neue Auflösungsstufe zwischen
  Bereitstellungsvorlage und OE-/Gruppen-Merkmal-Overrides.
- fetch_auftragskatalog_state()/_listing(): device_merkmale-Override-Ebene
  entfernt (gewann bisher immer), select_auftrag/deselect_auftrag-
  Endpunkte + set_auftrag_selection() entfernt. get_device_auftragskatalog
  bleibt als reine Ist-Zustand-Anzeige.
- Neuer Endpunkt GET /api/v1/organizations/{id}/workspaces (global +
  eigene, org-gefiltert - fetch_workspaces() bleibt admin-only, würde
  sonst fremde private Workspaces preisgeben).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 10:43:34 +02:00
adminandClaude Sonnet 5 b270103eab Fix: optionen-Update überschrieb bisherige Werte statt sie zu ergänzen + OE-/Gruppen-Select-Endpunkte akzeptieren jetzt optionen-Payload
Zwei zusammenhängende Fixes, beide Voraussetzung für die neue
optionen-Eingabe-UI in kundenplattform (struktur.py/gruppen.py):

1. set_auftrag_selection()/set_oe_auswahl()/set_gruppe_auswahl()
   ersetzten optionen bisher komplett (kein natives JSONB-Merge im
   UPSERT) - ein im Formular leer gelassenes Passwort-Feld
   (parse_optionen_form() lässt solche Keys bewusst weg, siehe
   optionen_felder.py) hätte ein zuvor gesetztes Passwort beim nächsten
   Bearbeiten stillschweigend gelöscht. Neue merge_optionen()-Funktion:
   bestehenden Wert holen, mit den neu eingegebenen Feldern
   zusammenführen, erst dann verschlüsseln (encrypt_sensitive_optionen()
   dafür idempotent gemacht - erkennt bereits "enc:"-präfigierte Werte
   und verschlüsselt nicht doppelt).

2. /api/v1/organisationseinheiten/{oe_id}/merkmale/{merkmal_key}/select
   und die Gruppen-Entsprechung nahmen bisher gar keinen Payload-Body
   entgegen (hartcodiert leeres optionen-Dict) - jetzt wie der
   geräteweise Endpunkt mit AuftragSelectRequest. Ohne diesen Fix hätte
   die neue OE-/Gruppen-UI (Kundenplattform) eingegebene AD-Domäne o.ä.
   nie beim Server ankommen können.

Zusätzlich: fetch_oe_merkmale_listing()/fetch_gruppe_merkmale_listing()
liefern jetzt auch optionen (maskiert, siehe mask_sensitive_optionen())
für die "bereits gesetzt"-Anzeige im Formular.

ECHT gegen die Produktions-DB getestet (nicht nur Code-Review): OE-Ebene
erst mit Domain+Passwort setzen, dann NUR die Domain erneut setzen ohne
Passwort-Feld - Passwort blieb korrekt erhalten, Domain wurde aktualisiert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 09:38:41 +02:00
adminandClaude Sonnet 5 d970936b7c Migration: AD-Anbindung/Fileserver/Update-Proxy-Merkmale
Siehe admin/ansible-content fuer die zugehoerigen Rollen und
admin/kundenplattform fuer die optionen-Eingabe-UI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 09:38:02 +02:00
adminandClaude Sonnet 5 d61071335d Fix: OE-/Gruppen-Optionen erreichten nie das Gerät + At-Rest-Verschlüsselung für sensible Merkmal-Optionen
Fund beim Vorbereiten der AD/OIDC/Fileserver-Merkmale (10.09.2026):
resolve_katalog_overrides() selektierte nie die optionen-Spalte aus
oe_merkmale/gruppen_merkmale, obwohl set_oe_auswahl()/set_gruppe_auswahl()
sie langst speichern konnten - nur device_merkmale.optionen erreichte je
ein Geraet. Da die Geraete-Einzel-Ebene laut Entscheidung vom 09.09.2026
nicht der vorgesehene Ort fuer Merkmal-Konfiguration ist (siehe
project_phase2_app_store_merkmale-Memory), waere OE-/Gruppen-seitig
gesetzte Konfiguration (z.B. eine AD-Domaene) nie beim Geraet angekommen.

resolve_katalog_overrides() liefert jetzt (aktiv, quelle, quelle_id,
optionen) statt nur (aktiv, quelle, quelle_id) - beide Aufrufer
(fetch_auftragskatalog_state fuer den Agenten,
fetch_auftragskatalog_listing fuer die Kundenportal-Anzeige) angepasst.

Zusaetzlich neue At-Rest-Verschluesselung fuer optionen-Werte, deren Key
auf _passwort/_password/_secret/_token endet (Namenskonvention statt
Allowlist - neue Merkmale mit einem Zugangsdaten-Feld brauchen nur den
passenden Key-Namen, keine Codeaenderung). Gleiches Fernet-Muster wie
kundenplattform/encryption.py fuer den WLAN-PSK, aber eigenes Modul/eigener
Schluessel (TUXFLOTTE_ENCRYPTION_KEY) - provisioning-server muss bei jedem
Agent-Checkin entschluesseln koennen, anders als der einmalige ISO-Build-
Transfer dort. Kundenportal-Katalog-Listing zeigt sensible Werte nur als
maskierten Boolean (mask_sensitive_optionen()), nie im Klartext oder auch
nur verschluesselt im Browser.

Neue Abhaengigkeit: cryptography==50.0.0 (gleiche Version wie
kundenplattform), TUXFLOTTE_ENCRYPTION_KEY live auf anode gesetzt.

ECHT gegen die laufende Produktions-Datenbank getestet (nicht nur
Syntax-Check): OE-Ebene (verschluesselter Roh-DB-Wert, korrekter
Klartext im Agent-Checkin-Pfad, korrekt maskiert im Listing-Pfad) UND
Geraete-Ebene als Regressionstest (bestehender, meistgenutzter Pfad
funktioniert unveraendert weiter) - beide bestanden, Testdaten wieder
entfernt.

Voraussetzung fuer die anstehenden AD/OIDC/Fileserver-Merkmale (siehe
project_phase2_app_store_merkmale-Memory), die auf OE-/Gruppen-Ebene mit
echten Zugangsdaten konfiguriert werden sollen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 09:21:51 +02:00
adminandClaude Sonnet 5 8c8894b942 Migration: Konfiguration/Härtung-Kategorien + erste 10 Merkmale
Siehe admin/ansible-content für die zugehörigen Rollen und
Test-/Verifikationsdetails.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-10 09:07:46 +02:00
adminandClaude Sonnet 5 d7f14c9feb Webapp-Vorlagen (Zwei-Ebenen-Modell, Nutzer-Entscheidung 09.09.2026)
Ergaenzung zum bisherigen rein kundeneigenen workspace_webapps: Admin
kann jetzt zusaetzlich global wiederverwendbare Vorlagen anlegen
('bereitstellen und zur Auswahl stellen'), Kunden aktivieren sie pro
Workspace zusaetzlich zu weiterhin frei erstellbaren eigenen
Eintraegen ('Kunden koennen selbst welche erstellen').

Neue Tabelle webapp_templates (Migration 0033, diesmal korrekt ueber
TUXFLOTTE_DATABASE_URL statt sudo-postgres angewendet - siehe Lehre aus
dem vorherigen Owner-Bug). workspace_webapps bekommt ein nullable
template_id (ON DELETE SET NULL) zur Aktivierungs-Nachverfolgung.

activate_webapp_template() kopiert Name/URL/Icon-Datei PHYSISCH unter
einem neuen, vom eigenen Eintrag abgeleiteten Dateinamen - keine
laufende Referenz auf die Vorlagen-Icon-Datei, sonst wuerde ein
spaeteres Loeschen/Aktualisieren der Vorlage die schon aktivierte Kopie
mit betreffen.

Neue Endpunkte: /api/v1/webapp-templates (CRUD),
/api/v1/workspaces/{id}/webapp-templates/available (noch nicht
aktivierte Vorlagen fuer einen Workspace), .../activate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-09 17:42:30 +02:00
adminandClaude Sonnet 5 4b229a992b Webapp-Mechanismus (Phase 2): Server-seitige DB+API+Favicon-Fetch
Nachtrag: Migration 0030 (App-Store-Batch-1-Merkmale) war im letzten
Commit versehentlich nicht mit dabei - jetzt nachgeholt (git status hat
es aufgedeckt).

Neue Tabelle workspace_webapps (Migration 0032): kundeneigene
{name, url}-Eintraege pro Workspace, kein fester Katalog wie bei
Merkmalen - jede Organisation/jeder Workspace hat ihre/seine eigene
Liste (Nutzer-Anforderung 09.09.2026: 'der Kunde hat diverse URLs').

app.py-Aenderungen:
- fetch_favicon(): bestes-Effort-Ermittlung des Website-Favicons
  (erst <link rel=icon> per Regex aus dem HTML, sonst /favicon.ico an
  der Domain-Wurzel) - bewusst ohne neue pip-Abhaengigkeit (nur
  urllib, passt zum bisher sehr schlanken requirements.txt).
- CRUD-Endpunkte /api/v1/workspaces/{id}/webapps[/​{webapp_id}]
  (GET/POST/PUT/DELETE), Icon-Serving unter /webapp-icons/{filename}
  (unauthentifiziert wie /golden-images/, gleicher
  Path-Traversal-Schutz).
- Checkin-Response um 'webapps' erweitert (fetch_workspace_webapps(),
  gleiches assignments->bereitstellungsvorlagen-Join-Muster wie
  fetch_assigned_blueprints()).
- WICHTIG: Server komponiert nie eine eigene oeffentliche URL (ANODE_URL
  ist nur client-seitig bekannt, siehe agent.py/backend.sh) - Checkin
  liefert nur den relativen icon_path, der Agent haengt ANODE_URL davor.

Gehört zusammen mit admin/ansible-content (neue Rolle
webapp-shortcuts, echt end-to-end getestet) und admin/kundenplattform
(Admin-UI unter /admin/technik/workspaces/{id}, Icon-Proxy analog zum
bestehenden ISO-Download-Proxy-Muster).

Live auf anode angewendet: Migration 0032 (workspace_webapps-Tabelle).
app.py-Deployment (git pull + Neustart) folgt separat.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-09 14:58:51 +02:00
adminandClaude Sonnet 5 ead3535e72 Migration: 6 Sonderfall-App-Store-Merkmale (google-chrome, anydesk, kolibri, veyon, gdevelop, turbowarp)
Siehe admin/ansible-content für die zugehörigen Rollen und die
Recherche-/Verifikationsdetails pro App.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-09 14:44:40 +02:00
adminandClaude Sonnet 5 e7202585e9 Migration: App-Store-Unterkategorien + Backfill fehlender Golden-Image-Blueprints
Fund beim Start von Phase 2 (Merkmal-Ausbau): bestehende App-Store-
Merkmale (brave, google-earth, guest-session, libreoffice, onlyoffice)
waren nur fuer die alten Backends mint/fedora verdrahtet, nicht fuer
die inzwischen tatsaechlich genutzten Golden-Image-Backends
(mint-image/debian-image/ubuntu-image) - der Auftragskatalog lieferte
diese Merkmale fuer jedes real provisionierte Geraet nicht aus. Per
INSERT...SELECT nachgezogen (gleicher Ansible-Rollenname, alle Rollen
sind bereits distributionsunabhaengig).

Zusaetzlich acht neue App-Store-Unterkategorien fuer die anstehende
App-Auswahl (siehe Plan-Datei/Memory project_phase2_app_store_merkmale).
Bestehende vier Merkmale bleiben vorerst bei der groben 'app-store'-
Kategorie, Umsortierung ist ein separater Schritt.

Live auf anode angewendet + verifiziert (15 Blueprint-Zeilen, 8 neue
Kategorien).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLk93dhDMrkPXefVdknMV9
2026-09-09 13:20:58 +02:00
admin 9657b68b45 Migration: ubuntu-image Backend + Test-Bereitstellungsvorlage (Liebherr) 2026-09-09 08:10:56 +02:00
admin 81bda7c25e run_iso_build: nie mehr still auf status=running haengen bleiben 2026-09-08 18:06:07 +02:00
admin 16441a2410 Fix: RestrictSUIDSGID blockierte ISO-Build nach De-Root-Umzug (siehe Session-Notizen) 2026-09-08 17:12:27 +02:00
admin 75de784e32 Migration: debian-image Backend + Test-Bereitstellungsvorlage (Liebherr)
Registriert das neue backends/debian-image/backend.sh (siehe
tuxflotte-installer@63df40d) in der backends-Tabelle und legt eine
Test-/Entwicklungsvorlage "Tuxflotte-Debian-Entwicklung-v0.1" in der
Liebherr-Org/Entwickler-Workspace an, analog zur bestehenden
Tuxflotte-Mint-Entwicklung-v0.1 - fuer den anstehenden E2E-Nachweis von
Phase 1 (zweite Referenz-Distribution).

Noch NICHT auf anode angewendet (DB-Schreibzugriff braucht User-Freigabe,
siehe Session).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpNs8yZeQkHgMWx6WNT1QH
2026-09-08 16:04:09 +02:00
admin 7ee62407b7 Mehrplatten-Heuristik: target_disk_rank in Bereitstellungsvorlagen
Neues Feld installation_directives.partitioning.target_disk_rank (Default
1 = groesste Platte) auf CreateBereitstellungsvorlageRequest/
create_bereitstellungsvorlage() - ersetzt den bisherigen impliziten
Default in backends/mint-image/backend.sh ("erste Platte in
lsblk-Erkennungsreihenfolge", nicht deterministisch bei mehreren internen
Datentraegern). Migration 0026 setzt den neuen Default in der
partitioning-Spalte und zieht bestehende Zeilen nach.

Siehe platform-docs/roadmap/multi-distro-merkmal-workspace-creator.md,
Phase 0 (Mehrplatten-Heuristik), 08.09.2026 mit dem Nutzer entschieden.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpNs8yZeQkHgMWx6WNT1QH
2026-09-08 15:32:58 +02:00
adminandClaude Sonnet 5 7dcbf9fcd4 Deploy-Artefakte fuer De-Root nachgezogen (ADR-0027)
provisioning-server.service, provisioning-server-iso-build.sudoers und ein
erstes README.md - vorher existierte ueberhaupt kein Unit-File und keine
Deploy-Dokumentation im Repo, nur der Env-Datei-Pfad war erwaehnt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KMczXJkjyv59Ds3TsDw8H3
2026-09-07 18:12:32 +02:00
adminandClaude Sonnet 5 6aaa8b8080 De-Root-Umzug: build_customer_iso.sh-Aufruf per scoped sudo
provisioning-server laeuft auf der neuen anode-VM nicht mehr als root
(siehe kundenplattform.service als Vorbild fuer die Haertung). Der
ISO-Build braucht weiterhin root (CAP_CHOWN/CAP_FOWNER fuers Erhalten
der Datei-Eigentuemer im Squashfs, siehe Kommentar in
build_customer_iso.sh) - deshalb hier per eng gefasster NOPASSWD-
sudo-Regel (/etc/sudoers.d/provisioning-server-iso-build, nur genau
dieser Skriptpfad) statt den ganzen Dienst wieder privilegiert laufen
zu lassen. End-to-End getestet: echter ISO-Build ueber denselben
systemd-Namespace wie der Produktionsdienst, inkl. Loeschbarkeit der
root-erzeugten Ausgabedatei durch den provisioning-server-User.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KMczXJkjyv59Ds3TsDw8H3
2026-09-07 16:09:21 +02:00
admin b88a934082 activate: previously_installed-Flag fuer Re-Install-Schutz
Neue has_completed_installation() prueft device_installation_events auf
ein 'installation_completed'-Ereignis - device_created=False allein reicht
nicht (ein bekanntes Geraet koennte auch an einem frueheren Commit-Gate
abgebrochen sein). Wird jetzt im /api/v1/activate-Antwortobjekt
mitgeliefert (.device.previously_installed).

Nutzer-Wunsch (02.09.2026): "Was ist eigentlich, wenn der Stick bei einem
bereits installierten System erneut ausgeführt wird?" - bisher gar
nichts, der Auto-Modus haette die Zielplatte stillschweigend erneut
komplett geloescht. tuxflotte-installer liest dieses Feld jetzt in
25_installation_confirm.sh und erzwingt eine echte Rueckfrage, auch im
Auto-Modus (siehe dortigen Commit).
2026-09-02 08:04:40 +02:00
admin d826505bdb agent/bootstrap: Debug-Modus automatisch beim allerersten Bootstrap aktivieren
Vor dem UPDATE geprueft, ob agent_secret_issued_at noch NIE gesetzt war -
nur dann (echte Neuinstallation, nicht ein spaeterer Self-Heal-Re-
Bootstrap bei 401 auf checkin) wird debug_mode_until gleich mit auf
jetzt+DEBUG_MODE_DURATION_HOURS gesetzt.

Nutzer-Wunsch (02.09.2026): kurz nach dem ersten Boot des fertig
installierten Systems soll ein frisch zugewiesenes Merkmal ohne
stundenlange Wartezeit (normales Poll-Intervall: 6 Stunden) ankommen -
der Debug-Modus (60s-Intervall) ist dafuer schon vorhanden, sprang bisher
aber nie automatisch an.
2026-09-02 07:24:15 +02:00
admin c09aff49df devices: archivierte/deprovisionierte Geraete bei Reaktivierung entsperren
load_or_create_device() aktualisierte bei einem bekannten device_fingerprint
bisher nur hostname/last_seen - archived_at/deprovisioned_at blieben
unangetastet.

Live gefunden (02.09.2026, echter Hardware-Test): ein per "Gerät löschen"
archiviertes Geraet (archive_device(), Soft Delete - Historie bleibt
erhalten) blieb nach einer frischen Neuinstallation derselben physischen
Hardware (gleicher device_fingerprint, neue /api/v1/activate-Anfrage)
dauerhaft aus fetch_devices_for_organization() verschwunden, weil
archived_at nie zurueckgesetzt wurde - fuer den Nutzer sah es aus, als
waere das neu registrierte Geraet nie angekommen.

Ein erfolgreicher /api/v1/activate-Aufruf ist der eindeutigste mögliche
Beweis, dass ein Geraet wieder aktiv genutzt wird - setzt jetzt beide
Felder explizit auf NULL zurueck.
2026-09-01 17:54:02 +02:00
admin 51fbf0fa0e devices: Installations-Meilensteine/Fehlschlaege melden (device_installation_events)
Neue Tabelle (Migration 0025) + POST /api/v1/devices/{device_id}/
installation-events (device_fingerprint-authentifiziert statt Service-
Token - das Geraet hat waehrend der Installation noch kein agent_secret,
gleiches Vertrauensniveau wie /api/v1/activate) + GET-Gegenstueck
(Service-Token, fuer die Kundenplattform).

Nutzer-Wunsch (01.09.2026, nach dem ersten echten Hardware-Test):
Fehlschlaege waehrend der Installation sichtbar machen, ohne am
Bildschirm mitschreiben zu muessen ("das ist kein Flow"). Bewusst
getrennt von device_merkmal_events (andere Lebensphase, andere Auth,
kein Merkmal-Bezug) und zunaechst nur admin-/operatorseitig ausgewertet -
ob/wie umfangreich der Kunde das in seiner eigenen GUI sieht, ist noch
offen (Nutzer: "das können wir noch wann anders diskutieren").
2026-09-01 16:20:50 +02:00
admin fb87a2377a iso-builds: Dateigroesse speichern und ausliefern
Neue Spalte size_bytes (Migration 0024, analog zu sha256/Migration 0023) -
wird direkt nach erfolgreichem Bau einmalig berechnet (output_path.stat().
st_size) und zusammen mit sha256 gespeichert, kein Neuberechnen bei jedem
Seitenaufruf noetig.

Nutzer-Feedback (01.09.2026, echter Hardware-Test): "Bitte nach ISO-Build
auch die Größe anzeigen lassen" - kundenplattform zeigt sie jetzt auf
/installationsmedium an (iso_build.size_bytes | filesizeformat).
2026-09-01 16:04:42 +02:00
adminandClaude Sonnet 5 0acb1b6b81 feat(golden-images): unauthentifizierte Hosting-Route fuer Golden Images
Analog zur bestehenden /installers/fedora-workstation/ks.cfg-Route:
GET /golden-images/{filename} liefert eine Datei aus data/golden-images/
(Env-Override TUXFLOTTE_GOLDEN_IMAGES_DIR), ohne Auth-Token - das
Zielgeraet hat beim Laden des Golden Image in backend_launch() noch
keins. Kein personalisierter Inhalt (anders als die ISO-Build-Downloads),
deshalb bewusst keine Authentifizierung noetig. Path-Traversal ueber den
Dateinamen wird per Path(...).name-Vergleich abgelehnt (400).

Vorbereitung fuer ADR-0024 Phase 3 (backends/mint-image/backend.sh
GOLDEN_IMAGE_URL zeigt als naechstes hierher).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 09:56:15 +02:00
adminandClaude Opus 5 e88e0da0da SHA256-Pruefsumme fuer ISO-Builds
Siehe Fund #7 im heutigen Self-Service-Flow-Test (Memory). Migration 0023
(iso_builds.sha256), compute_file_sha256() haest die Datei einmalig direkt
nach erfolgreichem Bau (gestreamt, nicht komplett im Speicher - ISOs sind
mehrere GB), update_iso_build() nimmt den Wert jetzt entgegen. Ermoeglicht
Integritaetspruefung unabhaengig vom Kundenplattform-Download-Proxy-Pfad.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 11:29:56 +02:00
adminandClaude Opus 5 2f6ff909f9 Agent-Intervall 6h+Jitter, Debug-Modus, Health-Monitoring
Siehe neue ADR (platform-docs). Migration 0022 (devices.debug_mode_until +
6 Health-Spalten). AGENT_POLL_INTERVAL_SECONDS-Default 300 -> 21600 (6h,
Jitter kommt agentenseitig). agent_checkin() liefert dynamisch das kurze
Debug-Intervall statt des Standards, solange debug_mode_until in der
Zukunft liegt - reiner Zeitvergleich, kein Cron zum Zurücksetzen nötig.
Neue Endpunkte POST/DELETE /api/v1/devices/{id}/debug-mode. Check-in
nimmt optional health-Objekt entgegen (Disk/RAM/Uptime/Load), schreibt es
kombiniert mit agent_last_checkin in einem UPDATE. fetch_devices_for_
organization()/fetch_all_devices() liefern debug_mode_until + Health jetzt
mit aus (Kundenplattform braucht sie für Anzeige/Toggle-Zustand, kein
Zusatz-Request nötig).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 10:00:39 +02:00
admin eeb5879a46 Merge: ISO-Volume-ID-Feature (anderer Session-Strang) + ISO-Ablauf-Sweep zusammenführen
# Conflicts:
#	app.py
2026-08-26 09:53:25 +02:00
adminandClaude Opus 5 4dea06b0c6 ISO-Ablauf nach 7 Tagen: expires_at + periodischer Sweep
Siehe ADR-0014-Nachtrag. Migration 0021 (iso_builds.expires_at),
update_iso_build() setzt expires_at=finished_at+7d bei Erfolg,
cleanup_expired_iso_builds() + stündlicher Sweep-Thread
(run_iso_build_expiry_sweep(), gestartet via @app.on_event('startup')).
Ergänzt die bestehende 'neuer Build löscht alte'-Logik, ersetzt sie nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 09:52:00 +02:00
adminandClaude Sonnet 5 c0877548ec feat: personalisierte ISO-Volume-ID aus Organisationsname + Bau-Datum
compute_iso_volid() sanitisiert den Organisationsnamen ISO9660-konform
(Grossbuchstaben/Ziffern/Unterstrich, max. 32 Zeichen, NFKD-Fallback fuer
Umlaute, 'ORG'-Fallback falls kein ASCII-Zeichen uebrig bleibt) und haengt
das Bau-Datum (UTC) an. run_iso_build() reicht das Ergebnis als sechsten
Parameter an build_customer_iso.sh durch, statt des bisher fest codierten
'TUXFLOTTE'-Labels.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 09:01:53 +02:00
adminandClaude Sonnet 5 34e5254984 feat: Kontakt-/Stammdaten je Organisation (organizations)
Neue Spalten kontakt_name, adresse, telefon, kontakt_email, notizen -
bewusst nur einfache Kontaktdaten, keine Richtlinien-/Sicherheitsfelder
(Network Profile/Secret Reference bleiben laut ADR-0001 weiterhin
unspezifiziert, eigenständiges Thema). Bestehender PATCH-Endpoint
(/api/v1/organizations/{id}) wird nur breiter, keine neue Route.

fetch_organizations() (Liste, fuer den Admin-Bereich) und
fetch_organization() (Einzelabruf, fuer Self-Service) beide erweitert,
damit intern und im Kundenportal dieselben Daten sichtbar sind.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 07:39:50 +02:00
adminandClaude Sonnet 5 e5b7081955 feat: Herkunfts-ID + OE-Join für Admin-Geräteliste
resolve_katalog_overrides() liefert jetzt zusätzlich quelle_id (konkrete
oe_id/gruppe_id, die eine Auftragskatalog-Einstellung geliefert hat) statt
nur den Herkunfts-Typ - Grundlage für anklickbare Herkunfts-Links im
Kundenplattform-Auftragskatalog. fetch_auftragskatalog_listing() gibt
quelle_id mit aus, fetch_auftragskatalog_state() (Agent-Pfad) ignoriert
sie weiterhin.

fetch_all_devices() (Admin-Geräteliste) bekommt denselben OE-Join wie
fetch_devices_for_organization() letzte Session - Grundlage für OE-Spalte
in der Admin-Geräteliste.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 21:27:51 +02:00
adminandClaude Sonnet 5 a0df9f3ef9 fix: select-Endpunkte für OE-/Gruppen-Merkmale brauchen keinen Payload-Body
Anders als beim geräteweisen select (das optionen für Merkmale wie
browser-brave transportiert) bieten die OE-/Gruppen-Editoren keine
Merkmal-Optionen an. select_oe_merkmal()/select_gruppe_merkmal()
erwarteten trotzdem einen AuftragSelectRequest-Body - keiner der
Kundenplattform-Aufrufer sendet einen, jeder Aufruf schlug mit 422
fehl (gefunden bei der Live-Verifikation).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 16:53:47 +02:00
adminandClaude Sonnet 5 25e1c5525b feat: Organisationseinheiten (OEs) + Gruppen für Konfigurationsgruppen
Neues Datenmodell (Migration 0018/0019 + Backfill-Script):
- organisationseinheiten: Baum pro Organisation, ein Gerät gehört zu
  genau einer OE (devices.oe_id). Jede Organisation bekommt automatisch
  eine geschützte Standard-OE 'Neue Geräte' als Landeplatz für neu
  aktivierte Geräte (create_organization() legt sie mit an,
  load_or_create_device() weist sie neuen Geräten zu).
- gruppen + device_gruppen: flach, M:N - ein Gerät kann in mehreren
  Gruppen gleichzeitig sein.
- oe_merkmale/gruppen_merkmale: sparsame present/absent-Overrides,
  gleiches Muster wie device_merkmale.

Resolution-Algorithmus (resolve_katalog_overrides): Workspace-Default →
OE-Kette (spezifischste OE gewinnt, klassische Vererbung) → Gruppen
('mehr gewinnt' bei Widerspruch, OE-Kettenergebnis zählt als weitere
Stimme) → Geräte-Override (gewinnt immer, wie bisher).
fetch_auftragskatalog_listing() liefert zusätzlich die Herkunft
('workspace'/'oe'/'gruppe'/'geraet') für die Kundenportal-UI.

Neue Endpunkte: CRUD für OEs/Gruppen, Merkmale-Select/Deselect/Unset je
OE/Gruppe, Geräte-OE-Verschiebung, Geräte-Gruppen-Mitgliedschaft.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 16:40:34 +02:00
adminandClaude Sonnet 5 507bab7282 fix: agent_bootstrap verweigert deprovisionierte Geräte
Ohne dieses Gate würde der neue Self-Heal-Re-Bootstrap im Agenten (siehe
provisioning-agent) die De-Provisionierung wirkungslos machen: der Agent
hätte sich bei jedem 401 auf checkin einfach selbst ein neues Secret
geholt, ganz ohne dass jemand 'erneut provisionieren' ausgelöst hat.
reprovision_device() (löscht deprovisioned_at) ist jetzt die tatsächliche
Voraussetzung dafür, dass ein erneuter Bootstrap-Aufruf erfolgreich ist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 13:14:49 +02:00
adminandClaude Sonnet 5 382ec12245 feat: Geräte-Lebenszyklus (De-/Reprovisionieren, Archivieren) + Hardware-/Event-Read-Endpoints
Fundament für die neue Aktionen-Spalte im Kundenportal (/geraete):
- devices.deprovisioned_at / archived_at (Migration 0017)
- GET /api/v1/devices/{id}/hardware, GET .../events
- POST .../deprovision, .../reprovision, .../archive
- fetch_devices_for_organization/fetch_all_devices blenden archivierte
  Geräte aus, liefern deprovisioned_at mit

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 13:04:51 +02:00
adminandClaude Sonnet 5 fb175bf861 feat: Bereitstellungsvorlage-Erstellung per API (schliesst Phase-5-Luecke)
Bislang gab es dafuer keinen API-/UI-Weg - Bereitstellungsvorlagen kamen
nur per Seed-Migration (0005/0011) fuer die "Default Lab"-Organisation
zustande. Eine wirklich neue Kundenorganisation hatte dadurch eine leere
Vorlagenauswahl auf der Kundenplattform-Installationsmedium-Seite (in
Phase 5 als offene Luecke dokumentiert).

Neuer POST /api/v1/organizations/{id}/bereitstellungsvorlagen: waehlt
Workspace + Backend, einfache Root-Dateisystem-Wahl (single-scheme,
ext4/btrfs - ein custom-Schema mit extra_partitions bleibt Sache
direkter DB-Pflege), optional Default/Diskverschluesselung/Secure-Boot.
Beachtet den bestehenden Unique-Index (nur eine is_default=TRUE-Zeile je
Organisation) durch expliziten Zwei-Schritte-Reset, gleiches Muster wie
Migration 0011.

Lokal end-to-end verifiziert: frische Organisation ohne jede Vorlage,
zwei Vorlagen nacheinander angelegt (zweite mit is_default=true kippt
die erste korrekt auf false), Partitioning/Diskverschluesselung/
Secure-Boot-Flags korrekt in der DB, Kundenplattform-Installationsmedium-
Seite zeigt die neue Vorlage danach korrekt in der Auswahl.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 01:59:21 +02:00
adminandClaude Sonnet 5 122e9fb499 feat: Organisations-Einzelabruf + Enrollment-Session-Aufladen (Phase 5)
Zwei neue Endpunkte, die die Kundenplattform-Selfservice-UI (Phase 5)
braucht:

- GET /api/v1/organizations/{id}: Einzelabruf statt der bisherigen
  reinen Liste - die Liste zeigt alle Organisationen uebergreifend
  (bislang nur fuer den organisationsuebergreifenden Admin-Bereich
  genutzt) und waere fuer einen normalen Kunden ein Datenleck.

- PATCH /api/v1/enrollment-sessions/{id}: erhoeht Kontingent
  (additiv, "aufladen" statt "neu setzen") und/oder verlaengert die
  Gueltigkeit einer bestehenden Session. Eine bereits ausgeteilte,
  personalisierte Kunden-ISO bettet die Session-Id fest als
  Aktivierungscode ein - das Aufladen macht sie weiter nutzbar, ohne
  dass eine neue ISO gebaut/neu verteilt werden muss.

Lokal end-to-end verifiziert (siehe kundenplattform-Commit).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 01:36:23 +02:00
adminandClaude Sonnet 5 9fc2c5f52f fix: ISO_BUILD_OUTPUT_DIR ebenfalls absolut aufloesen (Phase 4)
Gleiche Ursache wie im vorherigen Commit (MINT_ISO_PATH): der als $2 an
build_customer_iso.sh uebergebene Ausgabepfad wird von xorriso relativ
zu dessen Subprocess-cwd (TUXFLOTTE_INSTALLER_DIR) aufgeloest, nicht
relativ zum eigenen Arbeitsverzeichnis - xorriso brach beim Live-Test
auf anode entsprechend mit "Cannot acquire drive" ab.

Lokal reproduziert (relative Pfade + abweichendes Subprocess-cwd) und
verifiziert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 00:58:51 +02:00
adminandClaude Sonnet 5 659f2c5196 fix: MINT_ISO_PATH absolut aufloesen (Phase 4)
run_iso_build() startet build_customer_iso.sh mit
cwd=TUXFLOTTE_INSTALLER_DIR - der bisherige relative Default-Pfad fuer
MINT_ISO_PATH wurde also dort statt relativ zum eigenen
Arbeitsverzeichnis gesucht. Live-Test auf anode ist deshalb sofort mit
"Quell-ISO nicht gefunden" fehlgeschlagen. Jetzt wird der Pfad beim
Laden per Path(...).resolve() absolut aufgeloest.

Lokal reproduziert und verifiziert (relativer Pfad + abweichendes
Subprocess-cwd wie auf anode), danach erfolgreich gegen anodes Live-
Service getestet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 00:52:24 +02:00
adminandClaude Sonnet 5 f69ed66267 feat: ISO-Build-Orchestrierung fuer personalisierte Kunden-ISOs (Phase 4)
Neue iso_builds-Tabelle (Migration 0016) + vier Endpunkte, die
tuxflotte-installer/scripts/build_customer_iso.sh als Hintergrund-Thread
anstossen: POST/GET .../iso-builds (Trigger + Liste), GET
/api/v1/iso-builds/{id} (Status-Poll) und .../download (fertige ISO
ausliefern). Aeltere abgeschlossene Builds derselben Organisation werden
nach einem erfolgreichen neuen Build automatisch aufgeraeumt (Datei +
DB-Zeile) - anodes Root-Dateisystem ist mit 28GB knapp bemessen.

WLAN-Zugangsdaten werden bewusst nicht in iso_builds gespeichert, nur
transient an build_customer_iso.sh durchgereicht.

Lokal end-to-end gegen einen Wegwerf-Postgres-Container verifiziert (alle
Migrationen 0001-0016, echter build_customer_iso.sh-Lauf gegen die
gecachte Mint-ISO, Status-Uebergaenge pending->running->completed,
Download-Endpunkt liefert byte-identische Datei, Cleanup-Logik entfernt
den vorherigen Build einer Organisation nach dem naechsten erfolgreichen
Bau). Testorganisation danach wieder geloescht.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 00:39:32 +02:00
adminandClaude Sonnet 5 896c22c537 feat: Enrollment-Session-Verbrauch, Organisations-PATCH, strukturierte Partitionierung (Phase 3)
/api/v1/activate akzeptiert jetzt zusätzlich Enrollment-Session-Codes
(die Session-id selbst dient als Bearer-Credential, kein neues
code-Feld nötig) neben den bestehenden organisationsweiten
Aktivierungscodes - fällt bei Nicht-UUID sofort auf den klassischen
Pfad zurück, kein Konflikt. Prüft Widerruf/Ablauf/Kontingent, zählt
devices_used bei Erfolg hoch. Ungültige Sessions melden denselben
invalid_activation_code-Fehlschlag wie ein ungültiger klassischer
Code - das bricht den Installer schon beim Server-Handshake ab, lange
vor jeder destruktiven Aktion (siehe ADR-0008), kein zusätzlicher
Mechanismus nötig.

Antwort-templates werden bei Enrollment-Session-Aktivierung auf die
eine zur Session gehörende Vorlage gefiltert und mit is_default=true
markiert - der bereits in tuxflotte-installer Phase 2 gebaute
Auto-Modus (wählt automatisch die als is_default markierte Vorlage)
funktioniert dadurch ohne jede Installer-Änderung korrekt.

organizations: neuer PATCH-Endpoint (nur Name, weitere Felder bewusst
noch offen).

bereitstellungsvorlagen.partitioning: von TEXT auf JSONB umgestellt,
bestehende "default"-Werte auf {"scheme": "single", "root_filesystem":
"ext4"} migriert - das ist der Vertrag, den tuxflotte-installers
backend_generate_config() (Phase 2) bereits erwartet.

Lokal gegen eine Wegwerf-Postgres mit allen Migrationen 0001-0015
verifiziert: Enrollment-Session-Aktivierung (Vorlagen-Filterung,
is_default-Erzwingung, devices_used-Zählung), Kontingent-Erschöpfung,
Widerruf, Ablauf, Rückfall auf klassische Aktivierungscodes
(Regression), PATCH organizations (Erfolg + 404), /resolve liefert
partitioning jetzt korrekt als JSON-Objekt statt String.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 16:56:46 +02:00
adminandClaude Sonnet 5 5237606f49 feat: Google Earth Pro als Katalog-Merkmal
Merkmal + Blueprints (fedora, mint -> ansible_role 'google-earth')
in der app-store-Kategorie, analog zu LibreOffice nicht Teil von
workspace_merkmale (kein Basisbedarf, Kunde wählt aktiv).

Lokal gegen eine Wegwerf-Postgres mit allen Migrationen 0001-0014
verifiziert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 09:21:23 +02:00
adminandClaude Sonnet 5 bde9e92a80 feat: LibreOffice als Katalog-Merkmal
Merkmal + Blueprints (fedora, mint -> ansible_role 'libreoffice',
siehe ansible-content) in der app-store-Kategorie neben OnlyOffice.
Bewusst nicht Teil von workspace_merkmale - kein Gerät bekommt beide
Suiten automatisch, Kunde wählt aktiv im Auftragskatalog.

Lokal gegen eine Wegwerf-Postgres mit allen Migrationen 0001-0013
verifiziert (Katalog-Query liefert state=absent für beide Backends).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 08:45:14 +02:00
adminandClaude Sonnet 5 1d9f51008e feat: Enrollment Sessions + Neugerät-Bestätigung (Kundenplattform-Admin)
Endpoints für den Admin-Bereich "Verwaltung -> Flows": Enrollment
Sessions anlegen/auflisten/widerrufen, unbestätigte Geräte je
Organisation auflisten (Geräte ohne Bereitstellungsvorlagen-Zuweisung),
Bereitstellungsvorlagen einer Organisation abrufen (bisher nur intern
in fetch_templates() für /activate genutzt, jetzt auch als eigener
Endpoint). Die Bestätigung selbst nutzt den bereits bestehenden
/api/v1/templates/{id}/resolve-Endpoint - kein neuer Mechanismus nötig.

Deckt den serverseitigen Teil aus ADR-0008 ab. Der Installer-seitige
Wartemechanismus (Auto-Modus-Gate pollt auf Freigabe) ist bewusst nicht
Teil dieser Änderung - separates Folgeprojekt in tuxflotte-installer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 09:32:39 +02:00
adminandClaude Sonnet 5 34f9c9b2ef feat: Merkmale/Kategorien/Workspaces verwalten (Kundenplattform-Admin)
CRUD-Endpoints für den Admin-Bereich "Verwaltung -> Technik/Datenbank":
Merkmale (inkl. Kategorie-Zuordnung, im_auftragskatalog-Flag),
Kategorien, Workspaces samt Zusammensetzung aus Merkmalen
(workspace_merkmale hinzufügen/entfernen - Kernbaustein des später
geplanten Workspace-Creators). Backends/Blueprints nur lesend, da deren
Änderung echten Ansible-Rollen-Code voraussetzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 09:20:53 +02:00
adminandClaude Sonnet 5 d7956bf8f7 feat: organisationsübergreifende Geräteliste (Kundenplattform-Admin)
Neuer Endpoint GET /api/v1/devices (alle Geräte, Organisationsname
gejoint) für den Admin-Bereich "Verwaltung -> Geräte".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 09:12:20 +02:00
adminandClaude Sonnet 5 e043233aea feat: Organisationen + Aktivierungscodes verwalten (Kundenplattform-Admin)
Neue Endpoints GET/POST /api/v1/organizations und GET/POST
/api/v1/organizations/{id}/activation-codes für den neuen Kunden-
Management-Bereich der Kundenplattform. Aktivierungscodes werden
serverseitig generiert (kein manuelles Ausdenken nötig).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 09:03:17 +02:00
adminandClaude Sonnet 5 1e9020c5ba feat: enrollment_sessions Tabelle (ADR-0008)
Datenmodell für zeitlich begrenzte, organisationsgebundene Vorab-
Freigaben für geplante Massenrollouts. Verwaltung (anlegen/auflisten/
widerrufen) folgt in einem späteren Schritt zusammen mit der
Neugerät-Bestätigung im Kundenplattform-Admin-Bereich. Der eigentliche
Verbrauch (Auto-Modus-Gate im Installer) ist bewusst nicht Teil dieser
Migration - separates, noch nicht begonnenes Folgeprojekt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 08:52:09 +02:00