41 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
0967f7369e feat: Kundenplattform-Service-Token + Organizations-Devices-Endpoint
Neue Env-Var TUXFLOTTE_KUNDENPLATTFORM_TOKEN, require_admin_token zu
require_service_token verallgemeinert (akzeptiert Admin- oder
Kundenplattform-Token). Neuer GET /api/v1/organizations/{id}/devices
Endpoint für Kundenplattforms Geräteliste + Besitz-Validierung (ADR-0011).
Bestehende Auftragskatalog-Endpoints akzeptieren jetzt beide Tokens.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 16:52:55 +02:00
dc1637de7e feat: Mint als Default-Bereitstellungsvorlage statt Fedora
Fedora-Installer wurde zurückgestellt, Fokus liegt auf Mint (siehe
Rollen-Umbenennung in ansible-content, Migration 0010). Neue Geräte ohne
explizite Vorlagen-Auswahl bekommen jetzt standardmäßig die
Mint-Bereitstellungsvorlage zugewiesen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 16:27:58 +02:00
6fe3b6f81b refactor: blueprints.ansible_role auf distro-unabhängige Rollennamen
Begleitet die Umbenennung in ansible-content (brave-fedora/brave-mint ->
brave, onlyoffice-fedora/onlyoffice-mint -> onlyoffice). Beide Backends
nutzen ab jetzt dieselbe Flatpak-basierte, distro-unabhängige Rolle statt
zweier getrennter (von denen die -mint-Variante nie implementiert war).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 16:20:06 +02:00
9022563f95 chore: data/ und *.bak-* Backups ignorieren
data/ ist Laufzeit-Gerätespeicher (z.B. Hardware-Snapshots), keine
Quelldatei. *.bak-* ist die etablierte Namenskonvention der bisherigen
scp-Backups vor jedem Deploy - beides soll git status auf anode sauber
halten, jetzt wo dort direkt gepullt statt gescpt wird.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 16:09:23 +02:00
922c29859f fix: --plaintext-Flag für user-Passwort in Test-Kickstart ergänzen
Ohne das Flag interpretiert Kickstart --password als bereits gehashten
Wert - das Klartext-Passwort würde fehlinterpretiert und der Login
schlägt fehl. War bereits live auf anode direkt gefixt, aber nie
committed (Fund beim Aufräumen des dortigen Checkouts).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 16:02:38 +02:00
05a80f0998 feat: Auftragskatalog-Auswahl-API und optionen-Passthrough
Neue Migrationen 0008 (device_merkmale.optionen JSONB) und 0009 (Kategorien
app-store/individuelle-systemkonfiguration, Katalogfähigkeit für
guest-session-ephemeral/browser-brave/office-onlyoffice). Check-in-Antwort
gibt optionen je Auftrag mit. Neue admin-token-geschützte Endpoints zum
Auflisten/Auswählen/Abwählen von Aufträgen pro Gerät.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 15:28:49 +02:00
dc9bc40991 fix: default Auftragskatalog state to workspace membership, not absent
fetch_auftragskatalog_state() hardcoded absent as the default when no
device_merkmale row exists (COALESCE(dm.aktiv, FALSE)), regardless of
whether the Merkmal is part of the device's workspace. Since the
catalog is meant to be the same pool of Merkmale that workspaces are
composed from (see ADR-0010's clarification), that default was wrong:
flagging an already workspace-composed, actively-in-use Merkmal as
catalog-eligible would silently remove it from every device using
that workspace on their next check-in, without anyone ever deselecting
it.

Now joins workspace_merkmale for the device's assigned workspace and
defaults to present when the Merkmal is a member, absent otherwise;
an explicit device_merkmale row always overrides that default in
either direction. Verified against anode: catalog-eligible + already
workspace-composed Merkmal now defaults to present with no
device_merkmale row, explicit aktiv=false overrides to absent,
explicit aktiv=true overrides back to present.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 10:06:45 +02:00
eb73f2b6b7 feat: extend agent check-in with Auftragskatalog state, add report endpoint
Check-in now also returns "auftraege": the full present/absent state
of every catalog-eligible Merkmal (im_auftragskatalog = true) for the
device's backend, derived statelessly from device_merkmale rather than
just the workspace-resolved blueprints list, which stays untouched.
This is what lets a deselected Auftrag actually be reverted once the
corresponding Ansible role grows an absent branch (ADR-0010) - the
agent doesn't have that branch yet, so this is additive and inert
until it does.

Adds POST /api/v1/agent/report so the agent can write execution
results (applied/apply_failed/removed/remove_failed) back into
device_merkmal_events; selected/deselected is intentionally rejected
here since those are meant to come from wherever Auftrag selection
ends up being triggered, not from the agent itself.

Migration 0007 adds the backing tables (kategorien, device_merkmale,
device_merkmal_events) and the two new merkmale columns.

Verified against anode: checkin toggles present/absent correctly
after inserting a device_merkmale row, report endpoint accepts valid
entries and rejects unknown merkmale / non-agent event types / bad
secrets, and the written event round-trips through device_merkmal_events
correctly as jsonb.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 09:17:19 +02:00
341ef92a7a feat: add provisioning agent bootstrap and check-in endpoints
Adds POST /api/v1/agent/bootstrap (issues a hashed agent secret for a
device) and POST /api/v1/agent/checkin (Bearer-authenticated, resolves
the device's assigned blueprints via its Bereitstellungsvorlage and
returns them alongside the ansible-content repo URL and poll
interval). Migration 0006 adds the backing columns on devices
(agent_secret_hash, agent_secret_issued_at, agent_last_checkin).

This was implemented and verified end to end (activate -> resolve ->
bootstrap -> checkin, plus a full QEMU agent test) in an earlier
session but never committed to this repo, even though it has been
running in production on anode since. Committing now to close that
gap before building on top of it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 09:16:58 +02:00
e02b646564 fix: return backend/workspace key instead of internal UUID in resolved Runtime Blueprint
runtime_blueprint.backend_id/workspace_id must be the stable, distro-neutral
key (e.g. "fedora") as documented in 08-provisioning-api.md, not the
internal database UUID - the installer's backend dispatch
(backends/${backend_id}/backend.sh) depends on it being a directory-safe
key.
2026-07-18 10:48:50 +02:00
11186dfdf9 feat: migrate provisioning API to Merkmal/Blueprint/Bereitstellungsvorlage model
Replaces the config.json-backed flat profile model with the
Postgres-backed Workspace/Merkmal/Backend/Blueprint/Bereitstellungsvorlage
schema (migrations 0003-0005). /api/v1/activate now returns Bereitstellungsvorlage
templates instead of profiles, and a new POST /api/v1/templates/{id}/resolve
endpoint resolves a chosen template to a full Runtime Blueprint (workspace,
backend, resolved Merkmal->Ansible-role blueprints, and installation
directives), recording the resulting device assignment.

Removes the now-unused config.json and file-based device registry
directory in favor of the PostgreSQL-backed activation codes and
device tables.
2026-07-18 10:48:29 +02:00
02e2b9de94 refactor: expose device registration status 2026-07-14 13:04:39 +02:00
9c773e7b9b feat: persist hardware snapshots during activation 2026-07-13 09:00:02 +02:00
ba2dccff57 fix: correct PostgreSQL device row mapping 2026-07-12 15:32:43 +02:00
2683b65ffc feat: migrate device registry to PostgreSQL 2026-07-12 15:22:22 +02:00
8d70236ef5 feat: add PostgreSQL persistence foundation 2026-07-12 14:33:31 +02:00
de51278d69 feat: add initial device registry 2026-07-10 16:54:17 +02:00
root
9fca3675b7 Update Fedora kickstart localization and bootstrap 2026-07-03 09:59:46 +00:00
f3e85a9746 Switch service URLs to tuxflotte.de 2026-07-03 11:57:59 +02:00
root
e20a536002 Add Fedora kickstart installer endpoint 2026-06-16 16:38:38 +00:00
root
c61a2e5994 Initial provisioning server 2026-06-14 19:08:19 +00:00