openapi: 3.0.3
info:
  title: LCM REST API
  version: 1.29.0
  description: 'Die vollständige REST-API des LCM-Backends (Go/Fiber). Das Web-Frontend nutzt exakt diese
    Endpunkte - es gibt keine separate interne API.


    **Authentifizierung** (zwei gleichwertige Wege, siehe Sicherheitsschemas):

    - `Authorization: Bearer <jwt>` - Browser-/User-Sessions, ausgestellt von `POST /auth/login` (bzw.
    `/auth/login/2fa` bei aktivierter 2FA).

    - `X-API-Key: <key>` - Service-zu-Service-Zugriff mit einem unter *Einstellungen → API-Keys* erzeugten,
    auf Scopes eingeschränkten Schlüssel.


    Berechtigungen werden pro Endpunkt per RBAC-Scope geprüft (in der Beschreibung als `bereich:read`/`bereich:write`
    vermerkt); die sichtbaren Daten werden bei Servern zusätzlich auf die Gruppen des jeweiligen Benutzers
    eingeschränkt.


    Diese Referenz ist automatisch aus den Routen- und Handler-Definitionen des Quellcodes erzeugt (`internal/api/router/router.go`
    + Controller) - Pfade, Methoden und Berechtigungen sind exakt der aktuelle Stand. Nicht jeder Endpunkt
    hat ein vollständig dokumentiertes Response-Schema; die tatsächliche Antwort richtet sich nach dem
    jeweiligen Domain-Modell (siehe Architektur).'
servers:
- url: /api/v1
  description: Relativ zur LCM-Instanz, z. B. https://lcm.example.com/api/v1
components:
  securitySchemes:
    sessionAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Session-Token aus POST /auth/login.
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: Service-API-Key aus Einstellungen → API-Keys.
tags:
- name: API-Keys
- name: Alarme
- name: Alarmregeln
- name: Anwendungen
- name: Auth
- name: Benachrichtigungskanäle
- name: Benutzer (LCM-Login)
- name: Berechtigungsprofile
- name: Custom-Aktionen
- name: Docker (global)
- name: Einstellungen
- name: Jobs & Audit
- name: Linux-Benutzer
- name: Pakete (global)
- name: Regeln
- name: Rollen
- name: SSH-Sitzungen
- name: Server
- name: Server-Benutzer
- name: Servergruppen
- name: Sicherheit (global)
- name: System
- name: Zeitpläne
paths:
  /api/v1/agent/download/{arch}:
    get:
      tags:
      - System
      summary: lcm-agent-Binary herunterladen (LCM Remote).
      operationId: controllers_AgentDownload
      description: 'Liefert das lcm-agent-Binary für die manuelle Installation auf
        einem Server, der sich AUSGEHEND verbinden soll (NAT/Roaming).


        Bewusst ÖFFENTLICH - kein Login nötig und von der IP-Allowlist
        ausgenommen: Das Binary ist ein veröffentlichtes Artefakt ohne
        Geheimnisse, und der Zielserver hat beim Einrichten noch gar keine
        Zugangsdaten. Das Geheimnis ist das Enrollment-Token, nicht das Binary.


        Keine Berechtigung nötig.'
      parameters:
      - name: arch
        in: path
        required: true
        description: Zielarchitektur.
        schema:
          type: string
          enum:
          - amd64
          - arm64
      security: []
      responses:
        '200':
          description: Binary.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '404':
          description: Für diese Architektur liegt kein Binary vor.
  /api/v1/alert-events:
    get:
      tags:
      - Alarme
      summary: Events
      operationId: alertCtrl_Events
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `alerts:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/alert-rules:
    get:
      tags:
      - Alarmregeln
      summary: List
      operationId: alertCtrl_List
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `alerts:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Alarmregeln
      summary: Create
      operationId: alertCtrl_Create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                type:
                  type: string
                enabled:
                  type: boolean
                group_ids:
                  type: array
                  items:
                    type: integer
                channel_id:
                  type: string
                severity:
                  type: string
                threshold_percent:
                  type: integer
                forecast_days:
                  type: integer
                max_outdated:
                  type: integer
                min_severity:
                  type: string
                heartbeat_hours:
                  type: integer
                cooldown_minutes:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `alerts:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/alert-rules/{id}:
    patch:
      tags:
      - Alarmregeln
      summary: Update
      operationId: alertCtrl_Update
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                type:
                  type: string
                enabled:
                  type: boolean
                group_ids:
                  type: array
                  items:
                    type: integer
                channel_id:
                  type: string
                severity:
                  type: string
                threshold_percent:
                  type: integer
                forecast_days:
                  type: integer
                max_outdated:
                  type: integer
                min_severity:
                  type: string
                heartbeat_hours:
                  type: integer
                cooldown_minutes:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `alerts:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    delete:
      tags:
      - Alarmregeln
      summary: Delete
      operationId: alertCtrl_Delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `alerts:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/alerts/evaluate:
    post:
      tags:
      - Alarme
      summary: Löst eine sofortige Auswertung aller Alarm-Regeln aus.
      operationId: alertCtrl_Evaluate
      description: 'Löst eine sofortige Auswertung aller Alarm-Regeln aus.


        Benötigte Berechtigung: `alerts:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/apikeys:
    get:
      tags:
      - API-Keys
      summary: List
      operationId: apiKeyCtrl_List
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `apikeys:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - API-Keys
      summary: Der Klartext-Key ist NUR in dieser Response enthalten.
      operationId: apiKeyCtrl_Create
      description: 'Der Klartext-Key ist NUR in dieser Response enthalten.


        Benötigte Berechtigung: `apikeys:manage`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                scope:
                  type: string
                expires_in_days:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/apikeys/{id}:
    delete:
      tags:
      - API-Keys
      summary: Revoke
      operationId: apiKeyCtrl_Revoke
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: >-
        Zweistufig: der erste Aufruf WIDERRUFT den Key (sofort ungültig, der
        Eintrag bleibt mit revoked=true als Historie sichtbar); ein zweiter
        Aufruf auf dem bereits widerrufenen Key entfernt ihn endgültig.


        Benötigte Berechtigung: `apikeys:manage`.
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/apps:
    get:
      tags:
      - Anwendungen
      summary: 'Anwendungskatalog: Steckbriefe der Software neben der Paketverwaltung.'
      operationId: appCtrl_ListApps
      description: 'Liefert alle Katalogeintraege, mitgelieferte zuerst. Jeder Eintrag beschreibt, woran eine Anwendung erkannt wird, wie ihre Version zu ermitteln ist und wo die neueste steht.

        

        

        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Anwendungen
      summary: Eigenen Katalogeintrag anlegen.
      operationId: appCtrl_CreateApp
      description: 'Legt einen eigenen Eintrag an. Mindestens ein Merkmal ist erforderlich; Merkmale duerfen keine Sonderzeichen enthalten, weil sie beim Scan in eine Kommandozeile eingesetzt werden.

        

        

        Benötigte Berechtigung: `settings:manage`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                slug:
                  type: string
                name:
                  type: string
                description:
                  type: string
                enabled:
                  type: boolean
                markers:
                  type: string
                  description: Eine Merkmal-Zeile je Zeile, "art wert" (path|unit|bin|proc).
                version_command:
                  type: string
                  description: 'Der Platzhalter {path} wird durch den Fundort ersetzt.'
                version_pattern:
                  type: string
                compare:
                  type: string
                  enum: [semver, exact, none]
                latest_source:
                  type: string
                  description: 'github:owner/repo oder url:https://…'
                latest_pattern:
                  type: string
                backup_action_id:
                  type: integer
                update_action_id:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/apps/{id}:
    delete:
      tags:
      - Anwendungen
      summary: Eigenen Katalogeintrag entfernen.
      operationId: appCtrl_DeleteApp
      description: 'Entfernt einen selbst angelegten Eintrag samt der Funde, die auf ihn zeigen. Mitgelieferte Eintraege lassen sich abschalten, aber nicht loeschen (403).

        

        

        Benötigte Berechtigung: `settings:manage`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    put:
      tags:
      - Anwendungen
      summary: Katalogeintrag aendern.
      operationId: appCtrl_UpdateApp
      description: 'Aendert einen Eintrag. Bei mitgelieferten Eintraegen werden die Erkennungsfelder beim naechsten Start wieder auf den Auslieferungsstand gesetzt; Schalter und Aktions-Verweise bleiben erhalten.

        

        

        Benötigte Berechtigung: `settings:manage`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                slug:
                  type: string
                name:
                  type: string
                description:
                  type: string
                enabled:
                  type: boolean
                markers:
                  type: string
                  description: Eine Merkmal-Zeile je Zeile, "art wert" (path|unit|bin|proc).
                version_command:
                  type: string
                  description: 'Der Platzhalter {path} wird durch den Fundort ersetzt.'
                version_pattern:
                  type: string
                compare:
                  type: string
                  enum: [semver, exact, none]
                latest_source:
                  type: string
                  description: 'github:owner/repo oder url:https://…'
                latest_pattern:
                  type: string
                backup_action_id:
                  type: integer
                update_action_id:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/audit:
    get:
      tags:
      - Jobs & Audit
      summary: Audit Log
      operationId: jobCtrl_AuditLog
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `audit:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/auth/2fa/disable:
    post:
      tags:
      - Auth
      summary: Disable T O T P
      operationId: authCtrl_DisableTOTP
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/auth/2fa/enable:
    post:
      tags:
      - Auth
      summary: Enable T O T P
      operationId: authCtrl_EnableTOTP
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/auth/2fa/setup:
    post:
      tags:
      - Auth
      summary: Setup T O T P
      operationId: authCtrl_SetupTOTP
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/auth/login:
    post:
      tags:
      - Auth
      summary: 'Erster Schritt: Prüft Credentials.'
      operationId: authCtrl_Login
      description: 'Erster Schritt: Prüft Credentials. Hat der User 2FA aktiviert, wird KEIN reguläres
        Token ausgegeben, sondern eine Challenge - der zweite Schritt (/auth/login/2fa) verifiziert den
        TOTP-Code.


        Öffentlicher Endpunkt (keine Authentifizierung nötig).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
                password:
                  type: string
      security: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/auth/login/2fa:
    post:
      tags:
      - Auth
      summary: 'Zweiter Schritt: Verifiziert den TOTP-Code gegen die Challenge und liefert das finale
        JWT.'
      operationId: authCtrl_LoginTOTP
      description: 'Zweiter Schritt: Verifiziert den TOTP-Code gegen die Challenge und liefert das finale
        JWT.


        Öffentlicher Endpunkt (keine Authentifizierung nötig).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                challenge:
                  type: string
                code:
                  type: string
      security: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/auth/logout:
    post:
      tags:
      - Auth
      summary: JWTs sind zustandslos; der Client verwirft das Token.
      operationId: authCtrl_Logout
      description: JWTs sind zustandslos; der Client verwirft das Token. Der Endpunkt existiert für ein
        konsistentes API und künftige Token-Sperrlisten.
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/auth/me:
    get:
      tags:
      - Auth
      summary: Me
      operationId: authCtrl_Me
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/auth/password-reset:
    post:
      tags:
      - Auth
      summary: Stößt den Self-Service-Passwort-Reset per E-Mail an.
      operationId: authCtrl_RequestPasswordReset
      description: 'Stößt den Self-Service-Passwort-Reset per E-Mail an. Die Antwort ist IMMER dieselbe
        - ob die Adresse existiert, ist nach außen nicht erkennbar (keine User-Enumeration). Pro Client-IP
        gedrosselt: hier zählt JEDER Aufruf als Versuch, nicht nur Fehlversuche - der Endpunkt ist anonym
        und löst Mail-Versand aus.


        Öffentlicher Endpunkt (keine Authentifizierung nötig).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
      security: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/custom-actions:
    get:
      tags:
      - Custom-Aktionen
      summary: List
      operationId: customActionCtrl_List
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Custom-Aktionen
      summary: Create
      operationId: customActionCtrl_Create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                commands:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/custom-actions/{id}:
    patch:
      tags:
      - Custom-Aktionen
      summary: Update
      operationId: customActionCtrl_Update
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                commands:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    delete:
      tags:
      - Custom-Aktionen
      summary: Delete
      operationId: customActionCtrl_Delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/docker/compose:
    get:
      tags:
      - Docker (global)
      summary: Compose-Projekte ueber alle sichtbaren Server.
      operationId: opsCtrl_DockerCompose
      description: 'Die Compose-Projekte ueber alle sichtbaren Server hinweg.

        

        

        Benötigte Berechtigung: `packages:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/docker/containers:
    get:
      tags:
      - Docker (global)
      summary: Container aller sichtbaren Server.
      operationId: opsCtrl_DockerContainers
      description: 'Alle Container der sichtbaren Server, laufende zuerst.

        

        

        Benötigte Berechtigung: `packages:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/docker/overview:
    get:
      tags:
      - Docker (global)
      summary: Einzigartige Docker-Images über alle sichtbaren Server (Update-/CVE-Status).
      operationId: opsCtrl_DockerOverview
      description: 'Einzigartige Docker-Images über alle sichtbaren Server (Update-/CVE-Status).


        Benötigte Berechtigung: `packages:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/docs:
    get:
      tags:
      - System
      summary: Verzeichnis der eingebetteten Dokumentation.
      operationId: docsCtrl_List
      description: 'Listet die im Binary mitgelieferten Hilfeseiten. Erfordert eine Anmeldung, aber keine besondere Berechtigung.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/docs/{slug}:
    get:
      tags:
      - System
      summary: Eine Seite der eingebetteten Dokumentation.
      operationId: docsCtrl_Get
      description: 'Liefert eine einzelne Hilfeseite. Erfordert eine Anmeldung, aber keine besondere Berechtigung.'
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/health:
    get:
      tags:
      - System
      summary: Health-Check
      operationId: health
      security: []
      description: Öffentlicher Verfügbarkeits-Check, z. B. für Service-Monitoring.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
  /api/v1/jobs/filter-options:
    get:
      tags:
      - Jobs & Audit
      summary: Distinkte Typen und Auslöser für die Filter-Dropdowns.
      operationId: jobCtrl_FilterOptions
      description: 'Distinkte Typen und Auslöser für die Filter-Dropdowns.


        Benötigte Berechtigung: `jobs:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/jobs/history:
    get:
      tags:
      - Jobs & Audit
      summary: Gefiltert und seitenweise.
      operationId: jobCtrl_History
      description: 'Gefiltert und seitenweise. Query-Parameter: server_id, status, type, q (Namenssuche),
        triggered_by, hide_health, from, to (Datum YYYY-MM-DD), page, page_size. Antwort: {items, total,
        page, page_size}.


        Benötigte Berechtigung: `jobs:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/jobs/{id}/abort:
    post:
      tags:
      - Jobs & Audit
      summary: Bricht einen laufenden Job manuell ab und hebt damit die Server-Sperre auf (Sicherheitsventil
        für hängende Läu
      operationId: jobCtrl_Abort
      description: 'Bricht einen laufenden Job manuell ab und hebt damit die Server-Sperre auf (Sicherheitsventil
        für hängende Läufe).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/jobs/{id}/ssh-output:
    get:
      tags:
      - Jobs & Audit
      summary: Console Output
      operationId: jobCtrl_ConsoleOutput
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `jobs:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/jobs/{id}/ssh-sessions:
    get:
      tags:
      - Jobs & Audit
      summary: Alle Verbindungen (inkl.
      operationId: sshLogCtrl_JobSessions
      description: 'Alle Verbindungen (inkl. Kommandos) einer Job-Ausführung.


        Benötigte Berechtigung: `jobs:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users:
    get:
      tags:
      - Linux-Benutzer
      summary: List
      operationId: linuxCtrl_List
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `linux-users:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Linux-Benutzer
      summary: Create
      operationId: linuxCtrl_Create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
                full_name:
                  type: string
                email:
                  type: string
                shell:
                  type: string
                sudo:
                  type: boolean
                active:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `linux-users:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/activation-links/consume:
    post:
      tags:
      - Linux-Benutzer
      summary: Der Mitarbeiter setzt Passwort und/oder SSH-Key.
      operationId: linuxCtrl_ConsumeActivation
      description: 'Der Mitarbeiter setzt Passwort und/oder SSH-Key. Mit generate_key erzeugt LCM das
        Schlüsselpaar und liefert den privaten Schlüssel einmalig zurück.


        Öffentlicher Endpunkt (keine Authentifizierung nötig).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                token:
                  type: string
                password:
                  type: string
                key_name:
                  type: string
                public_key:
                  type: string
                generate_key:
                  type: boolean
      security: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/keys/{keyId}:
    delete:
      tags:
      - Linux-Benutzer
      summary: Remove Key
      operationId: linuxCtrl_RemoveKey
      parameters:
      - name: keyId
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `linux-users:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/{id}:
    get:
      tags:
      - Linux-Benutzer
      summary: Get
      operationId: linuxCtrl_Get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `linux-users:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    patch:
      tags:
      - Linux-Benutzer
      summary: Update
      operationId: linuxCtrl_Update
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
                full_name:
                  type: string
                email:
                  type: string
                shell:
                  type: string
                sudo:
                  type: boolean
                active:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `linux-users:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    delete:
      tags:
      - Linux-Benutzer
      summary: Blockiert (409), solange der Benutzer noch Servern zugeordnet ist.
      operationId: linuxCtrl_Delete
      description: 'Blockiert (409), solange der Benutzer noch Servern zugeordnet ist.


        Benötigte Berechtigung: `linux-users:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/{id}/activation-links/generate:
    post:
      tags:
      - Linux-Benutzer
      summary: Liefert das einmalige Token für den Self-Service-Link.
      operationId: linuxCtrl_GenerateActivation
      description: 'Liefert das einmalige Token für den Self-Service-Link.


        Benötigte Berechtigung: `linux-users:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ttl_hours:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/{id}/assign-group:
    post:
      tags:
      - Linux-Benutzer
      summary: Assign Group
      operationId: linuxCtrl_AssignGroup
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                group_id:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `linux-users:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/{id}/group-assignments:
    get:
      tags:
      - Linux-Benutzer
      summary: Gruppen-Zuordnungen eines Linux-Benutzers.
      operationId: linuxCtrl_GroupAssignments
      description: 'Zeigt, ueber welche Servergruppen der Benutzer verteilt wird und welches Berechtigungsprofil dabei je Gruppe gilt.

        

        

        Benötigte Berechtigung: `linuxusers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/{id}/group-profile:
    post:
      tags:
      - Linux-Benutzer
      summary: Berechtigungsprofil fuer eine Gruppen-Zuordnung setzen.
      operationId: linuxCtrl_SetGroupProfile
      description: 'Setzt das Profil, das dieser Benutzer auf den Servern einer Gruppe bekommt. profile_id null faellt auf das Standardprofil des Benutzers zurueck.

        

        

        Benötigte Berechtigung: `linuxusers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                group_id:
                  type: integer
                profile_id:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/{id}/keys:
    post:
      tags:
      - Linux-Benutzer
      summary: Add Key
      operationId: linuxCtrl_AddKey
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                public_key:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `linux-users:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/{id}/keys/generate:
    post:
      tags:
      - Linux-Benutzer
      summary: 'Erzeugt serverseitig ein ed25519-Schlüsselpaar: Der Public Key wird beim Benutzer gespeichert,
        der private Sch'
      operationId: linuxCtrl_GenerateKey
      description: 'Erzeugt serverseitig ein ed25519-Schlüsselpaar: Der Public Key wird beim Benutzer
        gespeichert, der private Schlüssel geht EINMALIG zurück (Download) und wird nirgends persistiert.


        Benötigte Berechtigung: `linux-users:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                public_key:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/{id}/remove-from-servers:
    post:
      tags:
      - Linux-Benutzer
      summary: Deprovisioniert den Benutzer aktiv von allen Servern (userdel) und löst die Zuordnungen
        - Voraussetzung fürs L
      operationId: linuxCtrl_RemoveFromServers
      description: 'Deprovisioniert den Benutzer aktiv von allen Servern (userdel) und löst die Zuordnungen
        - Voraussetzung fürs Löschen.


        Benötigte Berechtigung: `linux-users:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/linux-users/{id}/remove-group:
    post:
      tags:
      - Linux-Benutzer
      summary: Remove Group
      operationId: linuxCtrl_RemoveGroup
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                group_id:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `linux-users:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/notification-channels:
    get:
      tags:
      - Benachrichtigungskanäle
      summary: List
      operationId: notificationCtrl_List
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `notifications:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Benachrichtigungskanäle
      summary: Create
      operationId: notificationCtrl_Create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                type:
                  type: string
                enabled:
                  type: boolean
                config:
                  type: string
                secret:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `notifications:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/notification-channels/{id}:
    patch:
      tags:
      - Benachrichtigungskanäle
      summary: Update
      operationId: notificationCtrl_Update
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                type:
                  type: string
                enabled:
                  type: boolean
                config:
                  type: string
                secret:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `notifications:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    delete:
      tags:
      - Benachrichtigungskanäle
      summary: Delete
      operationId: notificationCtrl_Delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `notifications:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/notification-channels/{id}/test:
    post:
      tags:
      - Benachrichtigungskanäle
      summary: Test
      operationId: notificationCtrl_Test
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `notifications:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/packages/overview:
    get:
      tags:
      - Pakete (global)
      summary: Package Overview
      operationId: opsCtrl_PackageOverview
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `packages:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/packages/vulnerable:
    get:
      tags:
      - Pakete (global)
      summary: Vulnerable Packages
      operationId: opsCtrl_VulnerablePackages
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `packages:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/privilege-profiles:
    get:
      tags:
      - Berechtigungsprofile
      summary: Alle Berechtigungsprofile.
      operationId: profileCtrl_List
      description: 'Liefert die Profile samt ihrer sudo-, Editier- und Verzeichnisregeln sowie der verwendeten Regelbausteine.

        

        

        Benötigte Berechtigung: `profiles:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Berechtigungsprofile
      summary: Berechtigungsprofil anlegen.
      operationId: profileCtrl_Create
      description: 'Legt ein Profil an. Regeln, die faktisch Root-Rechte vergeben (Shell-Metazeichen, Wildcards, root-aequivalente Binaries), werden mit 422 abgelehnt - es sei denn, die Regel setzt allow_root_equivalent ausdruecklich.

        

        

        Benötigte Berechtigung: `profiles:write`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                slug:
                  type: string
                description:
                  type: string
                account_type:
                  type: string
                sudo_rules:
                  type: array
                  items:
                    type: object
                edit_rules:
                  type: array
                  items:
                    type: object
                path_rules:
                  type: array
                  items:
                    type: object
                block_uses:
                  type: array
                  items:
                    type: object
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/privilege-profiles/{id}:
    delete:
      tags:
      - Berechtigungsprofile
      summary: Berechtigungsprofil loeschen.
      operationId: profileCtrl_Delete
      description: 'Loescht ein Profil. Ein Profil, das noch zugewiesen ist, wird mit 409 abgelehnt; mitgelieferte Profile sind geschuetzt (403).

        

        

        Benötigte Berechtigung: `profiles:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    get:
      tags:
      - Berechtigungsprofile
      summary: Ein Berechtigungsprofil.
      operationId: profileCtrl_Get
      description: 'Liefert ein einzelnes Profil samt aller Regeln.

        

        

        Benötigte Berechtigung: `profiles:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    patch:
      tags:
      - Berechtigungsprofile
      summary: Berechtigungsprofil aendern.
      operationId: profileCtrl_Update
      description: 'Aendert Kopfdaten und Regeln. Die Regeln werden ersetzt, nicht zusammengefuehrt: Ein Profil beschreibt einen Soll-Zustand.

        

        

        Benötigte Berechtigung: `profiles:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                account_type:
                  type: string
                sudo_rules:
                  type: array
                  items:
                    type: object
                edit_rules:
                  type: array
                  items:
                    type: object
                path_rules:
                  type: array
                  items:
                    type: object
                block_uses:
                  type: array
                  items:
                    type: object
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/privilege-profiles/{id}/clone:
    post:
      tags:
      - Berechtigungsprofile
      summary: Aenderbare Kopie eines Profils anlegen.
      operationId: profileCtrl_Clone
      description: 'Legt eine aenderbare Kopie an - der Weg, ein mitgeliefertes Profil anzupassen. Die Kopie bekommt eigene Regeln; das Original bleibt unberuehrt.

        

        

        Benötigte Berechtigung: `profiles:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                slug:
                  type: string
                name:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/profile-blocks:
    get:
      tags:
      - Berechtigungsprofile
      summary: Alle Regelbausteine.
      operationId: blockCtrl_List
      description: 'Liefert den Katalog der wiederverwendbaren Rechte-Vorlagen samt ihrer Varianten je Distributions-Familie.

        

        

        Benötigte Berechtigung: `profiles:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Berechtigungsprofile
      summary: Regelbaustein anlegen.
      operationId: blockCtrl_Create
      description: 'Legt einen Baustein an. Jede Variante durchlaeuft dieselbe Pruefung wie eine von Hand geschriebene sudo-Regel.

        

        

        Benötigte Berechtigung: `profiles:write`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                slug:
                  type: string
                description:
                  type: string
                params:
                  type: string
                variants:
                  type: array
                  items:
                    type: object
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/profile-blocks/{id}:
    delete:
      tags:
      - Berechtigungsprofile
      summary: Regelbaustein loeschen.
      operationId: blockCtrl_Delete
      description: 'Loescht einen Baustein. Ein Baustein, den noch ein Profil verwendet, wird mit 409 abgelehnt; mitgelieferte sind geschuetzt (403).

        

        

        Benötigte Berechtigung: `profiles:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    patch:
      tags:
      - Berechtigungsprofile
      summary: Regelbaustein aendern.
      operationId: blockCtrl_Update
      description: 'Aendert Kopfdaten und Varianten. Die Varianten werden ersetzt, nicht zusammengefuehrt.

        

        

        Benötigte Berechtigung: `profiles:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                params:
                  type: string
                variants:
                  type: array
                  items:
                    type: object
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/profile-blocks/{id}/clone:
    post:
      tags:
      - Berechtigungsprofile
      summary: Aenderbare Kopie eines Bausteins anlegen.
      operationId: blockCtrl_Clone
      description: 'Legt eine aenderbare Kopie an - der Weg, einen mitgelieferten Baustein anzupassen.

        

        

        Benötigte Berechtigung: `profiles:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                slug:
                  type: string
                name:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/profile-blocks/{id}/preview:
    post:
      tags:
      - Berechtigungsprofile
      summary: Zeigt die Zeilen, die der Baustein ergibt.
      operationId: blockCtrl_Preview
      description: 'Zeigt die Zeilen, die der Baustein fuer eine Distributions-Familie ergibt - bevor sie auf einem Server landen.

        

        

        Benötigte Berechtigung: `profiles:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                family:
                  type: string
                values:
                  type: object
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/profile-blocks/{id}/usage:
    get:
      tags:
      - Berechtigungsprofile
      summary: Verwendungsnachweis eines Bausteins.
      operationId: blockCtrl_Usage
      description: 'Welche Profile den Baustein verwenden - der Nachweis vor einer Aenderung.

        

        

        Benötigte Berechtigung: `profiles:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/roles:
    get:
      tags:
      - Rollen
      summary: List Roles
      operationId: userCtrl_ListRoles
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `roles:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/rules/{id}/disable:
    post:
      tags:
      - Regeln
      summary: Disable Rule
      operationId: groupCtrl_DisableRule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/rules/{id}/enable:
    post:
      tags:
      - Regeln
      summary: Enable Rule
      operationId: groupCtrl_EnableRule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/rules/{id}/remove:
    delete:
      tags:
      - Regeln
      summary: Remove Rule
      operationId: groupCtrl_RemoveRule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/rules/{id}/trigger-now:
    post:
      tags:
      - Regeln
      summary: Trigger Rule
      operationId: groupCtrl_TriggerRule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/rules:
    get:
      tags:
      - Regeln
      summary: List Rules
      operationId: groupCtrl_ListRules
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: >-
        Globale Regel-Sicht über alle sichtbaren Gruppen (Manager sehen nur
        die Regeln ihrer Gruppen).


        Benötigte Berechtigung: `rules:manage`.
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/rules/{id}/update:
    patch:
      tags:
      - Regeln
      summary: Update Rule
      operationId: groupCtrl_UpdateRule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                type:
                  type: string
                command:
                  type: string
                schedule_id:
                  type: string
                enforce:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/schedules/{id}/disable:
    post:
      tags:
      - Zeitpläne
      summary: Disable Schedule
      operationId: groupCtrl_DisableSchedule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/schedules/{id}/enable:
    post:
      tags:
      - Zeitpläne
      summary: Enable Schedule
      operationId: groupCtrl_EnableSchedule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/schedules/{id}/remove:
    delete:
      tags:
      - Zeitpläne
      summary: Remove Schedule
      operationId: groupCtrl_RemoveSchedule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/schedules/{id}/trigger-now:
    post:
      tags:
      - Zeitpläne
      summary: Führt alle Rules des Schedules sofort aus.
      operationId: groupCtrl_TriggerSchedule
      description: 'Führt alle Rules des Schedules sofort aus.


        Benötigte Berechtigung: `rules:manage`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/schedules/{id}/update:
    patch:
      tags:
      - Zeitpläne
      summary: Update Schedule
      operationId: groupCtrl_UpdateSchedule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                cron_expr:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/security/advisories:
    get:
      tags:
      - Sicherheit (global)
      summary: Befunde der Fruehwarnung.
      operationId: advCtrl_List
      description: 'Befunde ueber alle sichtbaren Server, kritischste zuerst. Parameter: page, page_size, min_severity und resolved=1 (Verlauf statt offener Faelle).

        

        

        Benötigte Berechtigung: `packages:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/security/advisories/mirror:
    post:
      tags:
      - Sicherheit (global)
      summary: Lokale OSV-Kopie sofort spiegeln.
      operationId: advCtrl_Mirror
      description: 'Spiegelt die lokale OSV-Kopie sofort. Braucht das Einstellungs-Recht: Der Lauf laedt zig Megabyte auf den LCM-Host.

        

        

        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/security/advisories/poll:
    post:
      tags:
      - Sicherheit (global)
      summary: Durchgang der Fruehwarnung sofort anstossen.
      operationId: advCtrl_Poll
      description: 'Stoesst einen Durchgang sofort an, statt bis zum naechsten Takt zu warten.

        

        

        Benötigte Berechtigung: `servers:write`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/security/advisories/status:
    get:
      tags:
      - Sicherheit (global)
      summary: Betriebszustand der Fruehwarnung.
      operationId: advCtrl_Status
      description: 'Betriebszustand der Fruehwarnung, ohne die Fundliste.

        

        

        Benötigte Berechtigung: `packages:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/security/advisories/{id}/acknowledge:
    post:
      tags:
      - Sicherheit (global)
      summary: Befund zur Kenntnis nehmen.
      operationId: advCtrl_Acknowledge
      description: 'Nimmt einen Befund zur Kenntnis: Er bleibt sichtbar, loest aber keinen Alarm mehr aus.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/security/caches:
    get:
      tags:
      - Sicherheit (global)
      summary: Trefferquote und Belegung der Zwischenspeicher.
      operationId: advCtrl_Caches
      description: 'Trefferquote und Belegung beider Zwischenspeicher: des Scan-Cache im Arbeitsspeicher und des Advisory-Cache in der Datenbank.

        

        

        Benötigte Berechtigung: `packages:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/security/scanner:
    get:
      tags:
      - Sicherheit (global)
      summary: Stand des CVE-Scanners (Version + Schwachstellen-Datenbank).
      operationId: opsCtrl_ScannerStatus
      description: 'Liefert Version des Scanners (Trivy) und den Stand seiner
        Schwachstellen-Datenbank samt Frische-Bewertung.


        Warum das ein eigener Endpunkt ist: Trivy lädt die Datenbank beim Scan
        selbst nach - aber nur mit Netzzugang. Scheitert das (abgeschotteter
        Host, Proxy, Rate-Limit der Registry), WARNT Trivy und scannt mit der
        alten Datenbank weiter. Das Ergebnis ist dann kein Fehler, sondern
        „keine Sicherheitslücken gefunden": eine drei Wochen alte Datenbank
        sieht aus wie ein sauberer Server. Erst der Zeitstempel trennt beides.


        Bezugsgröße der Frische ist updated_at (wann der Hersteller die
        Datenbank gebaut hat), NICHT downloaded_at - wer dieselbe alte Datenbank
        täglich neu holt, hat trotzdem alte Daten. Schwellen: ab 48 Stunden
        „stale", ab 7 Tagen „critical".


        Benötigte Berechtigung: `packages:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Scanner- und Datenbank-Stand.
          content:
            application/json:
              schema:
                type: object
                properties:
                  available:
                    type: boolean
                    description: Ist das Scanner-Binary vorhanden?
                  version:
                    type: string
                  updated_at:
                    type: string
                    format: date-time
                    description: Wann der Hersteller diese Datenbank gebaut hat (maßgeblich).
                  next_update:
                    type: string
                    format: date-time
                  downloaded_at:
                    type: string
                    format: date-time
                    description: Wann dieser Host sie geholt hat - nur zur Diagnose.
                  freshness:
                    type: string
                    enum:
                    - fresh
                    - stale
                    - critical
                    - unknown
                  age_hours:
                    type: integer
                  error:
                    type: string
                    description: Warum der Stand nicht ermittelbar war (z.B. nie geladen).
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/security/scanner/update-db:
    post:
      tags:
      - Sicherheit (global)
      summary: Schwachstellen-Datenbank nachladen.
      operationId: opsCtrl_UpdateCVEDB
      description: 'Lädt die Schwachstellen-Datenbank herunter, ohne zu scannen
        (`trivy --download-db-only`). Asynchroner Job (202 {status, job_id,
        job_name}) - damit ein Fehlschlag samt Ausgabe des Werkzeugs im
        Protokoll landet statt still zu verpuffen. Genau dort steht dann auch
        die Ursache (kein Netz, Proxy, Rate-Limit der Registry).


        Der Job hängt an keinem Server: Scanner und Datenbank liegen zentral
        auf dem LCM-Host.


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '202':
          description: Job gestartet.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Der CVE-Scanner ist auf dem LCM-Host nicht installiert.
  /api/v1/security/update-all:
    get:
      tags:
      - Sicherheit (global)
      summary: Liefert den Fortschritt eines laufenden „Alle VMs aktualisieren"-Laufs.
      operationId: opsCtrl_BulkUpdateStatus
      description: 'Liefert den Fortschritt eines laufenden „Alle VMs aktualisieren"-Laufs.


        Benötigte Berechtigung: `packages:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Sicherheit (global)
      summary: Stößt Security-Updates auf allen erreichbaren Servern im Scope an.
      operationId: opsCtrl_StartBulkUpdate
      description: 'Stößt Security-Updates auf allen erreichbaren Servern im Scope an. Läuft bereits eines,
        antwortet der Endpunkt mit 409 samt aktuellem Stand.


        Benötigte Berechtigung: `servers:write`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/security/vulnerabilities:
    get:
      tags:
      - Sicherheit (global)
      summary: Globale CVE-Übersicht über alle sichtbaren Server (kritischste zuerst).
      operationId: opsCtrl_Vulnerabilities
      description: 'Globale CVE-Übersicht über alle sichtbaren Server (kritischste zuerst).


        Benötigte Berechtigung: `packages:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups:
    get:
      tags:
      - Servergruppen
      summary: List
      operationId: groupCtrl_List
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `server-groups:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/create:
    post:
      tags:
      - Servergruppen
      summary: Create
      operationId: groupCtrl_Create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `server-groups:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/{id}:
    get:
      tags:
      - Servergruppen
      summary: Get
      operationId: groupCtrl_Get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `server-groups:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/{id}/assign-manager:
    post:
      tags:
      - Servergruppen
      summary: Benutzer als Betreuer der Gruppe eintragen.
      operationId: groupCtrl_AssignManager
      description: 'Trägt einen Benutzer als Betreuer ein. Das ist der Hebel der
        Mandantentrennung: Ein Benutzer mit der Manager-Rolle sieht ÜBERHAUPT
        NUR die Server der Gruppen, in denen er eingetragen ist - der Scope
        filtert jede Abfrage darauf.


        Benötigte Berechtigung: `groups:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - user_id
              properties:
                user_id:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Eingetragen.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '404':
          description: Gruppe oder Benutzer unbekannt.
  /api/v1/server-groups/{id}/assign-server:
    post:
      tags:
      - Servergruppen
      summary: Assign Server
      operationId: groupCtrl_AssignServer
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                server_id:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `server-groups:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/{id}/disband:
    post:
      tags:
      - Servergruppen
      summary: Disband
      operationId: groupCtrl_Disband
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `server-groups:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/{id}/remove-manager:
    post:
      tags:
      - Servergruppen
      summary: Betreuer aus der Gruppe entfernen.
      operationId: groupCtrl_RemoveManager
      description: 'Entfernt den Benutzer als Betreuer. Er verliert damit den
        Zugriff auf die Server dieser Gruppe.


        Benötigte Berechtigung: `groups:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - user_id
              properties:
                user_id:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Entfernt.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '404':
          description: Gruppe oder Benutzer unbekannt.
  /api/v1/server-groups/{id}/remove-server:
    post:
      tags:
      - Servergruppen
      summary: Remove Server
      operationId: groupCtrl_RemoveServer
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                server_id:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `server-groups:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/{id}/rules:
    get:
      tags:
      - Servergruppen
      summary: List Rules
      operationId: groupCtrl_ListRules
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/{id}/rules/define:
    post:
      tags:
      - Servergruppen
      summary: Define Rule
      operationId: groupCtrl_DefineRule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                type:
                  type: string
                command:
                  type: string
                schedule_id:
                  type: string
                enforce:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/{id}/schedules:
    get:
      tags:
      - Servergruppen
      summary: List Schedules
      operationId: groupCtrl_ListSchedules
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/{id}/schedules/define:
    post:
      tags:
      - Servergruppen
      summary: Define Schedule
      operationId: groupCtrl_DefineSchedule
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                cron_expr:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `rules:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/server-groups/{id}/settings:
    patch:
      tags:
      - Servergruppen
      summary: Update Settings
      operationId: groupCtrl_UpdateSettings
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `server-groups:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers:
    get:
      tags:
      - Server
      summary: List
      operationId: serverCtrl_List
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/agent:
    post:
      tags:
      - Server
      summary: Agent-Server anlegen (LCM Remote) und Enrollment-Token erhalten.
      operationId: serverCtrl_CreateAgent
      description: 'Legt einen Server an, der sich per lcm-agent AUSGEHEND über
        MQTT verbindet - für Maschinen hinter NAT oder mit wechselnder Adresse,
        die LCM nicht per SSH erreichen kann.


        Das Enrollment-Token wird EINMALIG in der Antwort geliefert; gespeichert
        wird nur sein Hash. Es lässt sich nicht erneut anzeigen, nur ersetzen
        (siehe /servers/{id}/agent-token). Der Server ist zunächst offline und
        geht online, sobald sich der Agent meldet.


        Benötigte Berechtigung: `servers:write`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              properties:
                name:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '201':
          description: Server angelegt; Token einmalig enthalten.
          content:
            application/json:
              schema:
                type: object
                properties:
                  server:
                    type: object
                  token:
                    type: string
                    description: Enrollment-Token - wird nie wieder angezeigt.
        '400':
          description: Name fehlt oder ungültiger Request-Body.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Ein Server mit diesem Namen existiert bereits.
  /api/v1/servers/join:
    post:
      tags:
      - Server
      summary: Join
      operationId: serverCtrl_Join
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                host:
                  type: string
                port:
                  type: integer
                login_user:
                  type: string
                login_password:
                  type: string
                auth_method:
                  type: string
                confirmed_fingerprint:
                  type: string
                restricted_access:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/dsm/probe:
    post:
      tags:
      - Server
      summary: Probe DSM
      operationId: serverCtrl_ProbeDSM
      description: >-
        Liest den SHA-256-Fingerprint des TLS-Zertifikats eines
        Synology-DSM-Geräts - zur Bestätigung im Onboarding, BEVOR
        Zugangsdaten übertragen werden (DSM liefert ab Werk ein
        selbstsigniertes Zertifikat; das Pinning ist hier der MitM-Schutz).


        Benötigte Berechtigung: `servers:write`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                host:
                  type: string
                port:
                  type: integer
                  description: DSM-HTTPS-Port; leer/0 = 5001.
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '502':
          description: DSM nicht erreichbar.
  /api/v1/servers/dsm:
    post:
      tags:
      - Server
      summary: Create DSM
      operationId: serverCtrl_CreateDSM
      description: >-
        Nimmt ein Synology-DSM-Gerät zur Überwachung über die DSM-Web-API auf
        (kein SSH, kein Service-User). LCM meldet sich an, erhebt Version,
        Updates, Pakete, Volumes, Zeit/NTP und die Befunde des
        DSM-Security-Advisors und legt das Gerät online an.


        Benötigte Berechtigung: `servers:write`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                host:
                  type: string
                port:
                  type: integer
                  description: DSM-HTTPS-Port; leer/0 = 5001.
                account:
                  type: string
                  description: >-
                    DSM-Konto der Administratorgruppe, moeglichst ohne
                    erzwungene 2FA (ein unbeaufsichtigter Scan kann keinen
                    Einmalcode liefern).
                password:
                  type: string
                confirmed_fingerprint:
                  type: string
                  description: >-
                    Der in der Oberflaeche bestaetigte
                    Zertifikats-Fingerprint (SHA-256).
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '201':
          description: Erfolgreich.
        '400':
          description: Ungueltige Eingabe oder Fingerprint-Abweichung.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Name oder Host bereits vergeben.
  /api/v1/servers/routeros:
    post:
      tags:
      - Server
      summary: Legt ein MikroTik-RouterOS-Gerät zur reinen Überwachung an (Versions-Aktualität).
      operationId: serverCtrl_CreateRouterOS
      description: 'Legt ein MikroTik-RouterOS-Gerät an. Passwort-Modus verbindet sofort und
        scannt; Key-Modus liefert public_key zum Import auf dem Router.


        Benötigte Berechtigung: `servers:write`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                host:
                  type: string
                port:
                  type: integer
                login_user:
                  type: string
                login_password:
                  type: string
                auth_method:
                  type: string
                confirmed_fingerprint:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '201':
          description: Erfolgreich angelegt.
        '400':
          description: Ungültige Eingabe / kein RouterOS erkannt.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Name/Host bereits vorhanden.
  /api/v1/servers/known-repos:
    get:
      tags:
      - Server
      summary: Katalog der bekannten Paketquellen (Docker, PostgreSQL, …) - pflegbar unter Einstellungen
        → Repositories.
      operationId: serverCtrl_KnownRepos
      description: 'Katalog der bekannten Paketquellen (Docker, PostgreSQL, …) - pflegbar unter Einstellungen
        → Repositories.


        Benötigte Berechtigung: `servers:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/probe:
    post:
      tags:
      - Server
      summary: Liest den Host-Key-Fingerprint für die Bestätigung im Join-Wizard.
      operationId: serverCtrl_Probe
      description: 'Liest den Host-Key-Fingerprint für die Bestätigung im Join-Wizard.


        Benötigte Berechtigung: `servers:write`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                host:
                  type: string
                port:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}:
    get:
      tags:
      - Server
      summary: Get
      operationId: serverCtrl_Get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/acl/install:
    post:
      tags:
      - Server
      summary: ACL-Unterstuetzung nachruesten.
      operationId: serverCtrl_InstallACLSupport
      description: 'Installiert das Paket acl auf dem Zielserver - Voraussetzung fuer die Verzeichnisrechte aus den Berechtigungsprofilen.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/active-job:
    get:
      tags:
      - Server
      summary: Liefert den aktuell laufenden Job des Servers (null, wenn keiner läuft) - die UI zeigt
        damit die Job-Sperre an
      operationId: serverCtrl_ActiveJob
      description: 'Liefert den aktuell laufenden Job des Servers (null, wenn keiner läuft) - die UI zeigt
        damit die Job-Sperre an und deaktiviert Aktions-Buttons.


        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/agent-token:
    post:
      tags:
      - Server
      summary: Agent-Token ersetzen (Verlust/Kompromittierung).
      operationId: serverCtrl_RegenerateAgentToken
      description: 'Erzeugt ein neues Enrollment-Token. Das alte ist SOFORT
        ungültig und eine bestehende Agent-Verbindung wird getrennt - der Agent
        auf dem Server muss mit dem neuen Token neu eingerichtet werden. Das
        Token erscheint einmalig in der Antwort.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Neues Token (einmalige Anzeige).
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Der Server ist kein Agent-Server.
  /api/v1/servers/{id}/apps:
    get:
      tags:
      - Anwendungen
      summary: Auf diesem Server erkannte Anwendungen.
      operationId: appCtrl_ServerApps
      description: 'Zwei Listen: die erkannten Katalog-Anwendungen mit Fundort und installierter Version, und die laufenden Dienste, deren Unit-Datei zu keinem Paket gehoert.

        

        

        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/apps/{slug}/{action}:
    post:
      tags:
      - Anwendungen
      summary: Sicherung oder Update einer erkannten Anwendung ausloesen.
      operationId: appCtrl_RunAppAction
      description: 'action ist backup oder update. Beim Update laeuft die hinterlegte Sicherung vorweg, sofern nicht with_backup=false gesetzt ist; schlaegt sie fehl, laeuft das Update nicht. Antwort: job_id.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: slug
        in: path
        required: true
        schema:
          type: string
      - name: action
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                with_backup:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/apt-cache/detail:
    get:
      tags:
      - Server
      summary: 'Statistik-/Einstellungsseite: Installationsstatus, Live-Erreichbarkeit, Transfer-Statistiken
        und der "permanen'
      operationId: serverCtrl_AptCacherDetail
      description: 'Statistik-/Einstellungsseite: Installationsstatus, Live-Erreichbarkeit, Transfer-Statistiken
        und der "permanentes Caching"-Schalter.


        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/apt-cache/permanent-cache:
    post:
      tags:
      - Server
      summary: Set Apt Cacher Permanent Cache
      operationId: serverCtrl_SetAptCacherPermanentCache
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/apt-cache/restart:
    post:
      tags:
      - Server
      summary: Restart Apt Cacher
      operationId: serverCtrl_RestartAptCacher
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/apt-proxy:
    post:
      tags:
      - Server
      summary: Bindet den Server an den zentralen APT-Cache an bzw.
      operationId: serverCtrl_ConfigureAptProxy
      description: 'Bindet den Server an den zentralen APT-Cache an bzw. löst ihn davon.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/assign-user:
    post:
      tags:
      - Server
      summary: Ordnet einen Linux-Benutzer direkt einem Server zu.
      operationId: provCtrl_AssignUser
      description: 'Ordnet einen Linux-Benutzer direkt einem Server zu.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/decommission:
    post:
      tags:
      - Server
      summary: Body {purge:true} bereinigt zusätzlich den Zielserver (User + LCM-Zugänge) vor dem Löschen.
      operationId: serverCtrl_Decommission
      description: 'Body {purge:true} bereinigt zusätzlich den Zielserver (User + LCM-Zugänge) vor dem
        Löschen. Ohne purge wird der Server nur aus dem LCM entfernt.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                purge:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/harden-suggestions:
    get:
      tags:
      - Server
      summary: Vorschlaege fuer das Abschotten.
      operationId: serverCtrl_HardenSuggestions
      description: 'Sucht die ueblichen Konfigurations- und Datenverzeichnisse installierter Dienste ab und schlaegt vor, welche sich abschotten lassen.

        

        

        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/hardened-paths:
    get:
      tags:
      - Server
      summary: Abgeschottete Verzeichnisse.
      operationId: serverCtrl_HardenedPaths
      description: 'Die von LCM abgeschotteten Verzeichnisse samt dem Zustand vor der Aenderung - die Grundlage fuer das Zuruecknehmen.

        

        

        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Server
      summary: Ein Verzeichnis abschotten.
      operationId: serverCtrl_HardenPath
      description: 'Nimmt einem Verzeichnis den Welt-Zugriff und uebergibt es der genannten Gruppe. unit ist optional und benennt den Dienst, der danach neu gestartet wird.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                path:
                  type: string
                group:
                  type: string
                unit:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/hardened-paths/bulk:
    post:
      tags:
      - Server
      summary: Mehrere Verzeichnisse abschotten.
      operationId: serverCtrl_HardenPathsBulk
      description: 'Schottet mehrere Verzeichnisse in einem Lauf ab - die Vorschlaege aus harden-suggestions.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                targets:
                  type: array
                  items:
                    type: object
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/hardened-paths/{pathId}:
    delete:
      tags:
      - Server
      summary: Abschottung zuruecknehmen.
      operationId: serverCtrl_RestorePath
      description: 'Stellt Rechte und Gruppe eines Verzeichnisses so wieder her, wie sie vor dem Abschotten waren.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: pathId
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/lcm-host/install-sandbox:
    post:
      tags:
      - Server
      summary: Sandbox des CVE-Scanners nachruesten.
      operationId: serverCtrl_InstallSandbox
      description: 'Ruestet die Sandbox des CVE-Scanners nach (bubblewrap) - fuer Hosts, auf denen Trivy vor der Sandbox eingerichtet wurde.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/listening-ports/scan:
    post:
      tags:
      - Server
      summary: Lauschende Dienste sofort erheben.
      operationId: serverCtrl_ScanListeningPorts
      description: 'Liest das Inventar lauschender Dienste sofort vom Server (ss/netstat) und liefert es zurueck - die Grundlage der Vorschlaege im Firewall-Dialog.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/repositories/revert-https:
    post:
      tags:
      - Server
      summary: https-Umstellung der Paketquellen zurueckstellen.
      operationId: serverCtrl_RevertRepositoriesHTTPS
      description: 'Stellt Paketquellen wieder auf http zurueck - nur die, die vor der LCM-Umstellung http waren. Ohne uris alle Kandidaten des Servers. Wie bei der Umstellung: Sicherung, Probe mit apt-get update, bei Fehlschlag alles zurueck.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                uris:
                  type: array
                  items:
                    type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/snaps/refresh:
    post:
      tags:
      - Server
      summary: Einzelne Snaps aktualisieren.
      operationId: serverCtrl_RefreshSnaps
      description: 'Aktualisiert die genannten Snaps als Job.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                names:
                  type: array
                  items:
                    type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/snaps/refresh-all:
    post:
      tags:
      - Server
      summary: Alle Snaps aktualisieren.
      operationId: serverCtrl_RefreshAllSnaps
      description: 'Aktualisiert alle Snaps des Servers (snap refresh) als Job.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/snaps/remove:
    post:
      tags:
      - Server
      summary: Snaps entfernen.
      operationId: serverCtrl_RemoveSnaps
      description: 'Entfernt die genannten Snaps. snapd und die core-Basen werden abgelehnt - ohne sie faellt die Snap-Verwaltung bzw. die Laufzeit aller uebrigen Snaps aus.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                names:
                  type: array
                  items:
                    type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/ssh-2fa:
    post:
      tags:
      - Server
      summary: SSH-2FA einrichten oder entfernen.
      operationId: serverCtrl_ConfigureSSH2FA
      description: 'Aktiviert bzw. entfernt SSH-2FA (TOTP neben dem SSH-Key) als Job.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enable:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/time-check:
    post:
      tags:
      - Server
      summary: Zeitzone, Zeitdienst und Uhrenversatz auslesen.
      operationId: serverCtrl_TimeState
      description: >-
        Liest den Zeit-Zustand frisch vom Server (rein lesend, ohne Sonderrechte) und speichert ihn: Zeitzone, laufender Zeitdienst, ob er die Uhr als synchronisiert meldet, die konfigurierten Zeitserver und die Abweichung gegenueber LCM. Die Abweichung enthaelt die SSH-Laufzeit und ist auf ein bis zwei Sekunden genau. Benoetigte Berechtigung -- servers:read.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Der Server mit aktualisiertem Zeit-Zustand.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '502':
          description: Zielsystem nicht erreichbar.
  /api/v1/servers/{id}/timezone:
    post:
      tags:
      - Server
      summary: Zeitzone des Servers setzen.
      operationId: serverCtrl_SetTimezone
      description: >-
        Setzt die Zeitzone und LIEST SIE ZURUECK. Meldet das System danach etwas anderes, gilt sie als nicht gesetzt (502) -- eine geschriebene Datei allein ist kein Beleg. Fehlt die Zone auf dem Zielsystem (tzdata nicht installiert), sagt die Meldung genau das. Benoetigte Berechtigung -- servers:write.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                timezone:
                  type: string
                  description: Zeitzone im IANA-Format.
                  example: Europe/Berlin
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Gesetzt und zurueckgelesen.
        '400':
          description: Ungueltige Zeitzone.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '502':
          description: Auf dem Zielsystem nicht gesetzt bzw. nicht bestaetigt.
  /api/v1/servers/{id}/ntp:
    post:
      tags:
      - Server
      summary: Zeitserver eintragen und Synchronisierung belegen.
      operationId: serverCtrl_ConfigureNTP
      description: >-
        Traegt die Zeitserver ein (chrony, systemd-timesyncd oder ntpd -- je nachdem was vorhanden ist), startet den Dienst und BELEGT die Synchronisierung. Bestehende server/pool-Zeilen werden ersetzt, der Rest der Datei bleibt, eine .lcm-bak-Sicherung entsteht. Bleibt der Nachweis im Zeitfenster aus, endet der Aufruf mit 502 und einer Meldung, die genau das sagt -- die Konfiguration bleibt dabei bestehen. Benoetigte Berechtigung -- servers:write.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                servers:
                  type: array
                  maxItems: 4
                  items:
                    type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Zeitserver gesetzt, Synchronisierung belegt.
        '400':
          description: Ungueltiger Zeitserver oder mehr als vier.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Container -- die Uhr kommt vom Host und ist hier nicht einstellbar.
        '502':
          description: Zeitserver eingetragen, Synchronisierung aber nicht nachweisbar.
  /api/v1/servers/{id}/deep-scan:
    get:
      tags:
      - Server
      summary: Bericht des letzten Deep Scans.
      operationId: serverCtrl_DeepScanReport
      description: 'Lesesicht: Befunde, kernel-bezogene CVEs und der
        Härtungs-Index (0-100). Der Endpunkt startet NICHTS - er liefert den
        zuletzt gespeicherten Stand samt Zeitstempel.


        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Bericht.
          content:
            application/json:
              schema:
                type: object
                properties:
                  deep_scan_at:
                    type: string
                    format: date-time
                  hardening_index:
                    type: integer
                  kernel_reboot_pending:
                    type: boolean
                  findings:
                    type: array
                    items:
                      type: object
                  kernel_vulns:
                    type: array
                    items:
                      type: object
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Server
      summary: Deep Scan anstoßen (Kernel-Reboot-Lücke, Kernel-CVEs, Härtungs-Audit).
      operationId: opsCtrl_DeepScanServer
      description: 'Stößt den Deep Scan auf dem Server an: offene
        Kernel-Reboot-Lücke (needrestart), kernel-bezogene CVEs und ein
        Härtungs-/Fehlkonfigurations-Audit (Lynis, sonst LCM-Eigenprüfungen).


        Läuft asynchron über den Scheduler und liefert daher KEINE Job-ID: Der
        Abschluss ist am geänderten Zeitstempel des Berichts (GET) erkennbar.
        Der Scan ist rein lesend und läuft auch im eingeschränkten Sudo-Modus.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Scan angestoßen.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/deep-scan/reports/{reportId}:
    get:
      tags:
      - Server
      summary: Ein einzelner, datierter Deep-Scan-Lauf samt seiner Befunde.
      operationId: serverCtrl_DeepScanReportDetail
      description: 'Jeder Deep Scan wird als eigener Bericht abgelegt. Dieser
        Endpunkt liefert einen davon vollstaendig - mit `is_new` je Befund
        (stand im vorhergehenden Lauf noch nicht) und `resolved_titles` (was
        seither verschwunden ist). Die Liste der Laeufe steht in
        `GET /servers/{id}/deep-scan` unter `reports`.


        Die Report-ID wird gegen den Server geprueft: eine fremde ID liefert
        404, nicht die Befunde eines anderen Servers.


        Benoetigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: reportId
        in: path
        required: true
        description: UUID des Laufs (aus `reports[].id`).
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Der Lauf samt Befunden.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  created_at:
                    type: string
                    format: date-time
                  critical:
                    type: integer
                  warnings:
                    type: integer
                  infos:
                    type: integer
                  new_findings:
                    type: integer
                    description: Befunde, die im vorhergehenden Lauf noch nicht standen (erster Lauf immer 0).
                  resolved_findings:
                    type: integer
                  resolved_titles:
                    type: string
                    description: JSON-Array der seit dem Vorlauf verschwundenen Befund-Titel.
                  hardening_index:
                    type: integer
                    nullable: true
                  tools:
                    type: string
                    description: Werkzeuge dieses Laufs, z.B. "needrestart,lynis".
                  findings:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        category:
                          type: string
                          enum: [kernel, restart, misconfig]
                        severity:
                          type: string
                          enum: [info, warning, critical]
                        title:
                          type: string
                        detail:
                          type: string
                        tool:
                          type: string
                        is_new:
                          type: boolean
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '404':
          description: Lauf nicht gefunden (oder gehoert zu einem anderen Server).
  /api/v1/servers/{id}/deep-scan/install-tools:
    post:
      tags:
      - Server
      summary: needrestart + lynis auf dem Server installieren.
      operationId: serverCtrl_InstallDeepScanTools
      description: 'Installiert die Werkzeuge, die den Deep Scan aussagekräftig
        machen. Bewusst ANGEBOTEN statt automatisch: Es sind zusätzliche Pakete
        auf einem fremden System. Fehlen sie, läuft der Deep Scan weiter - nur
        mit den eingebauten Eigenprüfungen statt mit Lynis. Asynchroner Job
        (202 {status, job_id, job_name}).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '202':
          description: Job gestartet.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/dns:
    post:
      tags:
      - Server
      summary: Nameserver des Servers setzen (max. 3).
      operationId: serverCtrl_ConfigureDNS
      description: 'Setzt bis zu drei Nameserver auf dem Server. Eine LEERE Liste
        gibt die DNS-Verwaltung wieder frei (LCM fasst sie dann nicht mehr an).


        Läuft SYNCHRON und mit Rollback: Ist die Namensauflösung nach dem
        Schreiben gebrochen, stellt LCM die vorherige Konfiguration wieder her -
        ein unerreichbarer Server wäre der teuerste denkbare Fehlversuch.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                servers:
                  type: array
                  description: IP-Adressen der Nameserver; leer = Verwaltung freigeben.
                  items:
                    type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Gesetzt.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  output:
                    type: string
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '422':
          description: Mehr als drei Nameserver oder ungültige IP.
        '502':
          description: Server nicht erreichbar.
  /api/v1/servers/{id}/dns-test:
    post:
      tags:
      - Server
      summary: Namensauflösung auf dem Server prüfen.
      operationId: serverCtrl_DNSTest
      description: 'Prüft die gepflegten Test-Domains auf dem Server und liefert
        ein dreistufiges Ergebnis: full (alle auflösbar), partial (nur ein Teil)
        oder none. Dazu die Einzelergebnisse und die tatsächlich aktiven
        Resolver. Synchron.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Testergebnis.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                    - full
                    - partial
                    - none
                  detail:
                    type: string
                  resolvers:
                    type: array
                    items:
                      type: string
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '502':
          description: Server nicht erreichbar.
  /api/v1/servers/{id}/docker:
    get:
      tags:
      - Server
      summary: Container (mit Compose-Zuordnung), Images und CVE-Zähler eines Servers.
      operationId: serverCtrl_Docker
      description: 'Container (mit Compose-Zuordnung), Images und CVE-Zähler eines Servers.


        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/docker/compose-update:
    post:
      tags:
      - Server
      summary: Projektname im Body (nie in der URL) und nur gegen das gespeicherte Inventar validiert
        ausführbar.
      operationId: serverCtrl_UpdateCompose
      description: 'Projektname im Body (nie in der URL) und nur gegen das gespeicherte Inventar validiert
        ausführbar.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                project:
                  type: string
                service:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/docker/cve-relevance:
    post:
      tags:
      - Server
      summary: Markiert einen Container als CVE-relevant (seine Image-CVEs zählen in der Status-Bewertung)
        bzw.
      operationId: serverCtrl_SetContainerCVERelevance
      description: 'Markiert einen Container als CVE-relevant (seine Image-CVEs zählen in der Status-Bewertung)
        bzw. hebt die Markierung auf. Antwortet mit dem aktualisierten Server.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                relevant:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/docker/prune:
    post:
      tags:
      - Server
      summary: Entfernt alle ungenutzten Images (docker image prune -af) auf einen Schlag.
      operationId: serverCtrl_PruneDockerImages
      description: 'Entfernt alle ungenutzten Images (docker image prune -af) auf einen Schlag.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/docker/pull:
    post:
      tags:
      - Server
      summary: Zieht das neueste Image des Tags (ohne Container-Neustart).
      operationId: serverCtrl_PullDockerImage
      description: 'Zieht das neueste Image des Tags (ohne Container-Neustart).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                image:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/docker/pull-all:
    post:
      tags:
      - Server
      summary: Zieht die neueste Version aller genutzten, getaggten Registry-Images (ein Job, docker pull
        je Image; keine Con
      operationId: serverCtrl_PullAllDockerImages
      description: 'Zieht die neueste Version aller genutzten, getaggten Registry-Images (ein Job, docker
        pull je Image; keine Container-Neustarts).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/docker/refresh:
    post:
      tags:
      - Server
      summary: Liest das Docker-Inventar neu ein (reiner Scan).
      operationId: serverCtrl_RefreshDocker
      description: 'Liest das Docker-Inventar neu ein (reiner Scan).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/docker/remove-image:
    post:
      tags:
      - Server
      summary: Löscht ein ungenutztes Image (docker rmi).
      operationId: serverCtrl_RemoveDockerImage
      description: 'Löscht ein ungenutztes Image (docker rmi).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                image:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/firewall:
    post:
      tags:
      - Server
      summary: Body {enable:bool, rules:[{port,proto,ip_version,allowlist_ids,source_ips,comment}], ssh_sources:{allowlist_ids,source_ips}, ports:"80,443"}.
      operationId: serverCtrl_ConfigureFirewall
      description: 'Body {enable:bool, rules:[{port,proto,ip_version,allowlist_ids,source_ips,comment}], ssh_sources:{allowlist_ids,source_ips}, ports:"80,443"}. enable=false
        deaktiviert die Firewall; rules sind die detaillierten Freigaben (proto tcp|udp, ip_version any|v4|v6,
        allowlist_ids = IDs benannter IP-Allowlists, source_ips = händische Quell-IPs/CIDRs -
        beides zusammen schränkt die Freigabe als Union auf diese Quellen ein; ohne beide gilt sie für alle Quellen;
        comment = freie Bemerkung, landet bei ufw/nftables im Regelsatz auf dem Zielsystem),
        ports ist der Legacy-CSV-Weg (nur TCP). Asynchroner Job - je nach Distribution
        wird das Firewall-Werkzeug (ufw/firewalld/nftables) erst installiert; Antwort 202 {status, job_id, job_name}.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '202':
          description: Job gestartet ({status, job_id, job_name}).
        '400':
          description: Ungültige Regeln oder Backend im eingeschränkten Modus nicht verwaltbar.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/harden-ssh:
    post:
      tags:
      - Server
      summary: Harden S S H
      operationId: serverCtrl_HardenSSH
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/hardware:
    get:
      tags:
      - Server
      summary: Hardware
      operationId: serverCtrl_Hardware
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/lcm-host/install-apt-cacher:
    post:
      tags:
      - Server
      summary: Install Apt Cacher
      operationId: serverCtrl_InstallAptCacher
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/lcm-host/install-crowdsec-lapi:
    post:
      tags:
      - Server
      summary: Installiert die CrowdSec-LAPI auf dem LCM-Host (optional mit Firewall-Bouncer)
        und verdrahtet die Zugangsdaten automatisch in die Einstellungen.
      operationId: serverCtrl_InstallCrowdSecLapi
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                bouncer:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/lcm-host/install-trivy:
    post:
      tags:
      - Server
      summary: Install Trivy
      operationId: serverCtrl_InstallTrivy
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/lcm-host/status:
    get:
      tags:
      - Server
      summary: Einrichtungszustand des LCM-Hosts (Trivy, apt-cacher-ng).
      operationId: serverCtrl_LcmHostStatus
      description: 'Einrichtungszustand des LCM-Hosts (Trivy, apt-cacher-ng).


        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/outdated-packages:
    get:
      tags:
      - Server
      summary: Outdated Packages
      operationId: serverCtrl_OutdatedPackages
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/packages:
    get:
      tags:
      - Server
      summary: Packages
      operationId: serverCtrl_Packages
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/packages/refresh:
    post:
      tags:
      - Server
      summary: Aktualisiert die Paketliste (apt-get update + Bestandsaufnahme).
      operationId: serverCtrl_RefreshPackages
      description: 'Aktualisiert die Paketliste (apt-get update + Bestandsaufnahme).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/packages/update:
    post:
      tags:
      - Server
      summary: Ist version gesetzt, muss names genau ein Paket enthalten (exakte Version, Downgrade erlaubt).
      operationId: serverCtrl_UpdatePackages
      description: 'Ist version gesetzt, muss names genau ein Paket enthalten (exakte Version, Downgrade
        erlaubt).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                names:
                  type: array
                  items:
                    type: string
                version:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/packages/autoremove:
    post:
      tags:
      - Server
      summary: Nicht mehr benötigte Pakete entfernen (apt autoremove und Pendants).
      operationId: serverCtrl_AutoremovePackages
      description: 'Entfernt automatisch installierte Pakete, die von keinem anderen
        Paket mehr benötigt werden (verwaiste Abhängigkeiten) - `apt autoremove`,
        `dnf autoremove`, zypper `packages --unneeded`, pacman-Orphans. Asynchroner
        Job (202 {status, job_id, job_name}).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '202':
          description: Job gestartet.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/packages/pins:
    get:
      tags:
      - Server
      summary: Paket-Pins dieses Servers (global + serverspezifisch).
      operationId: serverCtrl_ListPackagePins
      description: 'Liefert die globalen und die serverspezifischen Pins, ob das
        Feature hier verfügbar ist (auf Proxmox ausgenommen - dort verwaltet
        Proxmox die Kernel-Aufbewahrung selbst) und den Kernel-Vorschlag für die
        Paketverwaltung dieses Servers.


        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Pin-Sicht.
          content:
            application/json:
              schema:
                type: object
                properties:
                  global:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        server_id:
                          type: integer
                          description: 0 = global (gilt für alle Server).
                        name:
                          type: string
                        no_remove:
                          type: boolean
                        hold:
                          type: boolean
                        note:
                          type: string
                  server:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        server_id:
                          type: integer
                          description: 0 = global (gilt für alle Server).
                        name:
                          type: string
                        no_remove:
                          type: boolean
                        hold:
                          type: boolean
                        note:
                          type: string
                  available:
                    type: boolean
                  reason:
                    type: string
                  package_manager:
                    type: string
                  kernel_preset:
                    type: array
                    items:
                      type: string
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Server
      summary: Paket-Pin anlegen (global oder für diesen Server).
      operationId: serverCtrl_CreatePackagePin
      description: 'Legt einen Pin an. Ein Pin schützt ein Paket vor dem Aufräumen
        (Autoremove) und dem gezielten Entfernen (no_remove) und/oder friert seine
        Version ein (hold). Ein abschließendes `*` im Namen wirkt als
        Präfix-Muster (`linux-image-*`). Existiert derselbe Name im selben
        Geltungsbereich bereits, werden dessen Wirkungen aktualisiert.


        Der Pin wirkt auf dem Ziel, sobald er angewendet wird (eigener Endpunkt)
        oder der nächste Aufräum-Lauf läuft - dieser schreibt die Schutzdateien
        selbst, bevor er entfernt.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              properties:
                name:
                  type: string
                  description: Paketname oder Präfix-Muster mit abschließendem *.
                no_remove:
                  type: boolean
                  default: true
                hold:
                  type: boolean
                  default: false
                note:
                  type: string
                global:
                  type: boolean
                  description: true = gilt für ALLE Server.
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '201':
          description: Pin angelegt.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                  server_id:
                    type: integer
                  name:
                    type: string
                  no_remove:
                    type: boolean
                  hold:
                    type: boolean
                  note:
                    type: string
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Auf Proxmox-Systemen ausgenommen.
        '422':
          description: Ungültiger Paketname oder Pin ohne Wirkung.
  /api/v1/servers/{id}/packages/pins/apply:
    post:
      tags:
      - Server
      summary: Paket-Pins auf dem Server festschreiben.
      operationId: serverCtrl_ApplyPackagePins
      description: 'Schreibt die wirksamen Pins (global + serverspezifisch) auf
        dem Ziel fest - je Paketverwaltung mit deren eigenem Mittel
        (APT::NeverAutoRemove/apt-mark, dnf protected.d/versionlock, zypper
        addlock, pacman HoldPkg/IgnorePkg, apk hold). Idempotent: in LCM
        gelöschte Pins verschwinden dabei auch auf dem Server. Asynchroner Job
        (202 {status, job_id, job_name}).


        Braucht vollen Sudo-Zugriff (schreibt unter /etc).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '202':
          description: Job gestartet.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Auf Proxmox-Systemen ausgenommen oder eingeschränkter Modus.
  /api/v1/servers/{id}/packages/pins/kernel:
    post:
      tags:
      - Server
      summary: Ein-Klick-Kernelschutz (mehrere Kernel-Versionen behalten).
      operationId: serverCtrl_PinKernelPreset
      description: 'Legt die Kernel-Muster der Paketverwaltung dieses Servers als
        Pins an - bewusst als „nicht entfernen", NICHT als Versions-Fixierung:
        Der Zweck ist, ältere Kernel als Rückfallebene zu behalten, nicht neue zu
        verhindern. Body {global} steuert den Geltungsbereich.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                global:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '201':
          description: Pins angelegt.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Auf Proxmox-Systemen ausgenommen.
        '422':
          description: Für diese Paketverwaltung gibt es keinen Kernel-Vorschlag.
  /api/v1/servers/{id}/packages/pins/{pinId}:
    delete:
      tags:
      - Server
      summary: Paket-Pin entfernen.
      operationId: serverCtrl_DeletePackagePin
      description: 'Entfernt einen Pin. Das Paket kann danach wieder aufgeräumt
        werden - auf dem Ziel wirkt das, sobald die Pins erneut angewendet werden
        (oder der nächste Aufräum-Lauf läuft).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: pinId
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Pin entfernt.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '404':
          description: Pin unbekannt oder gehört zu einem anderen Server.
  /api/v1/servers/{id}/kernels/cleanup:
    post:
      tags:
      - Server
      summary: Entfernt die nicht mehr benötigten Kernel samt ihrer Begleitpakete.
      operationId: serverCtrl_RemoveOldKernels
      description: 'Entfernt die nicht mehr benötigten Kernel samt ihrer Begleitpakete. Laufender Kernel,
        neuere und eine Rückfallebene bleiben erhalten; der Aufruf braucht keine Liste.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '202':
          description: Job gestartet.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Nichts zu entfernen oder Paketverwaltung nicht unterstützt.
  /api/v1/servers/{id}/packages/remove:
    post:
      tags:
      - Server
      summary: Body {names:[…]}. Gezieltes Entfernen benannter Pakete.
      operationId: serverCtrl_RemovePackages
      description: 'Body {names:[…]}. Deinstalliert gezielt die genannten Pakete
        (`apt remove` und Pendants). Kritische Systempakete (SSH-Server, sudo,
        Paketverwaltung, Kernel, libc …) sind geschützt und werden mit 422
        abgelehnt. Asynchroner Job (202 {status, job_id, job_name}).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                names:
                  type: array
                  items:
                    type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '202':
          description: Job gestartet.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '422':
          description: Ungültiger oder geschützter Paketname.
  /api/v1/servers/{id}/packages/upgrade-all:
    post:
      tags:
      - Server
      summary: Upgrade All Packages
      operationId: serverCtrl_UpgradeAllPackages
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/packages/upgrade-security:
    post:
      tags:
      - Server
      summary: Upgrade Security Packages
      operationId: serverCtrl_UpgradeSecurityPackages
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/packages/{name}/versions:
    get:
      tags:
      - Server
      summary: Installierbare Versionen eines Pakets (neueste zuerst) für die Auswahl.
      operationId: serverCtrl_PackageVersions
      description: 'Installierbare Versionen eines Pakets (neueste zuerst) für die Auswahl.


        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: name
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/reboot:
    post:
      tags:
      - Server
      summary: Startet den Server neu.
      operationId: serverCtrl_Reboot
      description: 'Startet den Server neu. Braucht vollen Root-Zugriff (im eingeschränkten Sudo-Modus
        gesperrt).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/reconnect:
    post:
      tags:
      - Server
      summary: Stellt die Verbindung zu einem bestehenden Server neu her und überschreibt dessen Credentials
        (geänderte Anmel
      operationId: serverCtrl_Reconnect
      description: 'Stellt die Verbindung zu einem bestehenden Server neu her und überschreibt dessen
        Credentials (geänderte Anmeldedaten oder ausgetauschter Server).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                host:
                  type: string
                port:
                  type: integer
                login_user:
                  type: string
                login_password:
                  type: string
                auth_method:
                  type: string
                confirmed_fingerprint:
                  type: string
                restricted_access:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/refresh-all:
    post:
      tags:
      - Server
      summary: Liest Hardware, Pakete, Snaps, Repos, Docker, Speicher und den Firewall-/SSH-Status neu
        ein (reine Datenerfass
      operationId: serverCtrl_RefreshAll
      description: 'Liest Hardware, Pakete, Snaps, Repos, Docker, Speicher und den Firewall-/SSH-Status
        neu ein (reine Datenerfassung).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/refresh-hardware:
    post:
      tags:
      - Server
      summary: Liest Hardware-/OS-Fakten neu ein (kein Upgrade, kein Bestand-Replace).
      operationId: serverCtrl_RefreshHardware
      description: 'Liest Hardware-/OS-Fakten neu ein (kein Upgrade, kein Bestand-Replace).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/remove-user:
    post:
      tags:
      - Server
      summary: Remove User
      operationId: provCtrl_RemoveUser
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/repositories:
    get:
      tags:
      - Server
      summary: Repositories
      operationId: serverCtrl_Repositories
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/repositories/add:
    post:
      tags:
      - Server
      summary: Richtet eine bekannte Paketquelle aus dem Katalog ein.
      operationId: serverCtrl_AddRepository
      description: 'Richtet eine bekannte Paketquelle aus dem Katalog ein.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                key:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/repositories/secure:
    post:
      tags:
      - Server
      summary: Stellt alle http-Quellen auf https um (mit Rollback).
      operationId: serverCtrl_SecureRepositories
      description: 'Stellt alle http-Quellen auf https um (mit Rollback).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/restrict-sudo:
    post:
      tags:
      - Server
      summary: Schränkt den LCM-Management-Benutzer nachträglich auf die sudo-Whitelist ein (Einweg-Operation).
      operationId: serverCtrl_RestrictSudo
      description: 'Schränkt den LCM-Management-Benutzer nachträglich auf die sudo-Whitelist ein (Einweg-Operation).
        Ein bereits eingeschränkter Server liefert 409.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/rotate-key:
    post:
      tags:
      - Server
      summary: Rotate Key
      operationId: serverCtrl_RotateKey
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/security-tool:
    post:
      tags:
      - Server
      summary: fail2ban bzw. CrowdSec installieren und einrichten.
      operationId: serverCtrl_ConfigureSecurityTool
      description: 'Installiert und konfiguriert einen Brute-Force-Schutz auf dem
        Server. Asynchroner Job (202 {status, job_id, job_name}).


        Aussperr-Schutz: Die IP, mit der LCM den Server gerade erreicht, landet
        immer in der Allowlist (`ignoreip` bei fail2ban, Parser-Whitelist bei
        CrowdSec) - zusammen mit Loopback und den hier angegebenen Quellen. Die
        Quell-IP wird dafür ohne `sudo` gelesen (`$SSH_CONNECTION`), weil `sudo`
        diese Variable verwirft.


        Lässt sich eine referenzierte Allowlist nicht auflösen, bricht LCM den
        Schutz NICHT ab, hängt aber eine sichtbare `LCM-WARNUNG` an den
        Job-Output - niemand soll Schutz vermuten, den es nicht gibt.


        Die Einrichtung greift in Dienste und Systemkonfiguration ein und ist
        deshalb im eingeschränkten Modus (sudo-Whitelist) gesperrt. Für die
        Bedienung eines bereits installierten Werkzeugs siehe
        /security-tool/manage.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - tool
              properties:
                tool:
                  type: string
                  enum:
                  - fail2ban
                  - crowdsec
                allowlist_ips:
                  type: array
                  description: Zusätzliche Quellen, die nie gesperrt werden - IP oder CIDR.
                  items:
                    type: string
                allowlist_ids:
                  type: array
                  description: IDs benannter IP-Allowlists; ihre Einträge kommen hinzu.
                  items:
                    type: integer
                bouncer:
                  type: boolean
                  default: true
                  description: >-
                    Nur CrowdSec - Firewall-Bouncer mitinstallieren (setzt die Sperren erst durch).
                    Fehlt das Feld, gilt true: ohne Bouncer erkennt CrowdSec zwar, sperrt aber nichts.
                  example: true
                collections:
                  type: array
                  description: Nur CrowdSec - zu installierende Collections. Leer = crowdsecurity/sshd.
                  items:
                    type: string
                lapi_mode:
                  type: string
                  description: >-
                    Nur CrowdSec - LAPI-Anbindung. local = eigenständig auf diesem
                    Server (Vorgabe); remote/console setzen einen unter
                    Einstellungen hinterlegten Zugang voraus.
                  enum:
                  - local
                  - remote
                  - console
                  default: local
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '202':
          description: Job gestartet.
        '400':
          description: >-
            Unbekanntes Werkzeug, ungültige Allowlist-IP, ungültiger
            Collection-Name oder LAPI-Modus, oder der gewählte remote/console-Modus
            ist nicht hinterlegt.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: >-
            Eingeschränkter Modus (die Einrichtung braucht vollen Sudo-Zugriff),
            RouterOS-Gerät, oder CrowdSec ist auf dieser Distribution nicht
            verfügbar (pacman).
  /api/v1/servers/{id}/security-tool/bans:
    get:
      tags:
      - Server
      summary: Aktuelle Sperrliste eines installierten Sicherheits-Tools.
      operationId: serverCtrl_SecurityToolBans
      description: 'Liefert die aktuell gesperrten IP-Adressen von fail2ban bzw.
        CrowdSec, über beide Werkzeuge hinweg vereinheitlicht. Bewusst SYNCHRON
        (kein Job): Für eine reine Abfrage wäre Job-Polling unangemessen, und wer
        sich selbst ausgesperrt hat, will die Liste sofort sehen. Demo-Server
        liefern eine leere Liste (kein SSH-Kontakt).


        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: tool
        in: query
        required: true
        description: fail2ban | crowdsec
        schema:
          type: string
          enum:
          - fail2ban
          - crowdsec
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Sperrliste.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bans:
                    type: array
                    items:
                      type: object
                      properties:
                        ip:
                          type: string
                        scope:
                          type: string
                          description: 'fail2ban: Jail - CrowdSec: Szenario.'
                        since:
                          type: string
                          description: 'CrowdSec: verbleibende Dauer.'
                        cause:
                          type: string
                          description: 'CrowdSec: Ursprung der Entscheidung.'
                        tool:
                          type: string
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '422':
          description: Unbekanntes Werkzeug oder nicht unterstützter Server.
  /api/v1/servers/{id}/security-tool/manage:
    post:
      tags:
      - Server
      summary: Ein installiertes Sicherheits-Tool bedienen (Dienst, Allowlist, Sperren).
      operationId: serverCtrl_ManageSecurityTool
      description: 'Bedient ein BEREITS installiertes fail2ban bzw. CrowdSec:
        Dienst steuern (start/stop/restart/enable/disable), deinstallieren,
        Allowlist neu schreiben und nachladen oder eine Sperre aufheben.
        Asynchroner Job (202 {status, job_id, job_name}).


        Die Aktionen greifen in Dienste und Systemkonfiguration ein und sind
        deshalb im eingeschränkten Modus (sudo-Whitelist) gesperrt. Ist das
        Werkzeug auf dem Server nicht installiert, endet der Job mit einem
        Fehler statt einer irreführenden Erfolgsmeldung.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - tool
              - action
              properties:
                tool:
                  type: string
                  enum:
                  - fail2ban
                  - crowdsec
                action:
                  type: string
                  enum:
                  - start
                  - stop
                  - restart
                  - enable
                  - disable
                  - uninstall
                  - allowlist
                  - unban
                unban_ip:
                  type: string
                  description: Nur bei action=unban - die freizugebende Adresse.
                allowlist_ips:
                  type: array
                  description: Nur bei action=allowlist - zusätzliche freie IPs/CIDRs.
                  items:
                    type: string
                allowlist_ids:
                  type: array
                  description: Nur bei action=allowlist - IDs benannter IP-Allowlists.
                  items:
                    type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '202':
          description: Job gestartet.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung (im eingeschränkten Modus gesperrt).
        '422':
          description: Unbekanntes Werkzeug, unbekannte Aktion oder fehlende/ungültige IP.
  /api/v1/servers/{id}/settings:
    patch:
      tags:
      - Server
      summary: Update Settings
      operationId: serverCtrl_UpdateSettings
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                host:
                  type: string
                port:
                  type: integer
                user_sync_disabled:
                  type: string
                unreachable_uncritical:
                  type: string
                unreachable_grace_days:
                  type: string
                docker_updates_disabled:
                  type: boolean
                  description: >-
                    Keine neuen Image-Versionen einspielen (weder manuell noch
                    per Regel). Inventar und Update-Anzeige laufen weiter.
                docker_cves_ignored:
                  type: boolean
                  description: >-
                    CVE-Funde aus Container-Images dieses Servers zählen nicht
                    für Status/Alarme und erscheinen nicht in der
                    Sicherheitsübersicht. Sticht auch die Markierung einzelner
                    Container als CVE-relevant.
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/snaps:
    get:
      tags:
      - Server
      summary: Snaps
      operationId: serverCtrl_Snaps
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/ssh-port:
    post:
      tags:
      - Server
      summary: 'Body {port:2222}: stellt den SSH-Port um (erst verifizieren, dann übernehmen).'
      operationId: serverCtrl_ChangeSSHPort
      description: 'Body {port:2222}: stellt den SSH-Port um (erst verifizieren, dann übernehmen).


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/ssh-root-login:
    post:
      tags:
      - Server
      summary: 'Body {disabled:bool}: sperrt/erlaubt das direkte root-SSH-Login.'
      operationId: serverCtrl_SetSSHRootLogin
      description: 'Body {disabled:bool}: sperrt/erlaubt das direkte root-SSH-Login.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/ssh-sessions:
    get:
      tags:
      - Server
      summary: Server Sessions
      operationId: sshLogCtrl_ServerSessions
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `jobs:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/status:
    get:
      tags:
      - Server
      summary: Status
      operationId: serverCtrl_Status
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:read`.'
      responses:
        '200':
          description: Ampel-Status, Befunde, OS-Support und Kernel-Sicht.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                    - excellent
                    - green
                    - yellow
                    - red
                  insights:
                    type: array
                    description: >-
                      Befunde der Bewertung. Stufe "info" sind reine Hinweise -
                      sie erklaeren etwas, aendern die Ampel aber nicht.
                    items:
                      type: object
                      properties:
                        severity:
                          type: string
                          enum:
                          - info
                          - warning
                          - critical
                        message:
                          type: string
                  os_support:
                    type: object
                  docker_port_exposures:
                    type: array
                    items:
                      type: object
                  kernel:
                    type: object
                    description: >-
                      Laufender Kernel (uname -r) und installierte Kernel-Pakete.
                      Erst der Abgleich beider Angaben sagt etwas: Nach einem
                      Kernel-Update ist der neue installiert, laeuft aber erst
                      nach einem Neustart.
                    properties:
                      running:
                        type: string
                        description: Ausgabe von uname -r - der tatsaechlich gebootete Kernel.
                      installed:
                        type: array
                        description: >-
                          Installierte Kernel-Pakete, neueste zuerst. In
                          Containern LEER (siehe managed).
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                            release:
                              type: string
                            version:
                              type: string
                            running:
                              type: boolean
                      managed:
                        type: boolean
                        description: >-
                          false in Containern (LXC, Docker, …): dort laeuft der
                          Kernel des Hosts und ist von innen nicht verwaltbar.
                      container:
                        type: string
                        description: Erkannter Container-Typ, wenn managed=false.
                      reboot_pending:
                        type: boolean
                        description: Es ist ein neuerer Kernel installiert als der laufende.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/storage-history:
    get:
      tags:
      - Server
      summary: Täglicher Verlauf der Festplattenbelegung (Kapazität + Nutzung).
      operationId: serverCtrl_StorageHistory
      description: 'Täglicher Verlauf der Festplattenbelegung (Kapazität + Nutzung).


        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/sync-users:
    post:
      tags:
      - Server
      summary: Sync Users
      operationId: provCtrl_SyncUsers
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/unharden-ssh:
    post:
      tags:
      - Server
      summary: Unharden S S H
      operationId: serverCtrl_UnhardenSSH
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/users:
    get:
      tags:
      - Server-Benutzer
      summary: Anmeldefaehige Konten des Servers.
      operationId: provCtrl_ServerUsers
      description: 'Die beim letzten Scan erfassten anmeldefaehigen Linux-Konten.

        

        

        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/users/disable:
    post:
      tags:
      - Server-Benutzer
      summary: Konto auf dem Server sperren.
      operationId: provCtrl_DisableServerUser
      description: 'Sperrt das Konto auf dem Zielsystem, ohne es zu entfernen. root und der LCM-Dienstbenutzer sind geschuetzt.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/users/enable:
    post:
      tags:
      - Server-Benutzer
      summary: Gesperrtes Konto wieder freigeben.
      operationId: provCtrl_EnableServerUser
      description: 'Hebt die Sperre eines Kontos auf dem Zielsystem auf.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/users/pending:
    get:
      tags:
      - Server-Benutzer
      summary: Offene Benutzer-Abgleiche.
      operationId: provCtrl_PendingUserSyncs
      description: 'Was liegengeblieben ist, weil der Server im Moment der Aenderung nicht erreichbar war.

        

        

        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/users/refresh:
    post:
      tags:
      - Server-Benutzer
      summary: Konten jetzt neu erheben.
      operationId: provCtrl_RefreshServerUsers
      description: 'Erhebt die Konten jetzt neu ueber SSH.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/users/remove:
    post:
      tags:
      - Server-Benutzer
      summary: Konto endgueltig vom Server entfernen.
      operationId: provCtrl_RemoveServerUser
      description: 'Entfernt das Konto ENDGUELTIG vom Zielsystem (samt Home-Verzeichnis). Von LCM verwaltete Konten werden abgelehnt - sie gehoeren ueber die Linux-Benutzer entfernt.

        

        

        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/users/{username}/logins:
    get:
      tags:
      - Server-Benutzer
      summary: Anmelde-Historie eines Kontos.
      operationId: provCtrl_ServerUserLogins
      description: 'Die Anmelde-Historie eines Kontos (aus wtmp).

        

        

        Benötigte Berechtigung: `servers:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: username
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/vulnerabilities:
    get:
      tags:
      - Server
      summary: Vulnerabilities
      operationId: serverCtrl_Vulnerabilities
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `servers:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/servers/{id}/vulnerabilities/scan:
    post:
      tags:
      - Server
      summary: Stößt einen CVE-Scan für einen einzelnen Server an.
      operationId: opsCtrl_ScanServerVulnerabilities
      description: 'Stößt einen CVE-Scan für einen einzelnen Server an.


        Benötigte Berechtigung: `servers:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/apt-cache/overview:
    get:
      tags:
      - Einstellungen
      summary: Datengrundlage der zentralen APT-Cache-Seite.
      operationId: serverCtrl_AptCacheOverview
      description: 'Liefert die konfigurierte Cache-URL, ihre Live-Erreichbarkeit
        samt Transfer-Statistiken und - falls apt-cacher-ng auf dem LCM-Host
        läuft - dessen Verwaltbarkeit (Server-ID, permanentes Caching,
        Platten-Belegung).


        Nicht server-scoped: Der LCM-Host wird intern aufgelöst.


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Übersicht.
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/apt-cache/status:
    get:
      tags:
      - Einstellungen
      summary: Prüft vom LCM-Host aus, ob der konfigurierte apt-cacher-ng erreichbar ist.
      operationId: opsCtrl_AptCacheStatus
      description: 'Prüft vom LCM-Host aus, ob der konfigurierte apt-cacher-ng erreichbar ist.


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/crowdsec/status:
    get:
      tags:
      - Einstellungen
      summary: Prüft vom LCM-Host aus, ob die konfigurierte CrowdSec-LAPI erreichbar ist.
      operationId: opsCtrl_CrowdSecLapiStatus
      description: 'Prüft vom LCM-Host aus, ob die konfigurierte CrowdSec-LAPI erreichbar ist und die hinterlegten Maschinen-Zugangsdaten akzeptiert (Login-Probe auf /v1/watchers/login).


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/subscription:
    get:
      tags:
      - Einstellungen
      summary: Subscription-Status dieser Installation.
      operationId: subCtrl_Status
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Vertragsstand der Enterprise-Subscription: Kunde, Status (active/expired/revoked/unreachable),
        Ablaufdatum mit Resttagen, letzte Prüfung, Instanz-Kennung und der apt-Kanal-Zustand
        des LCM-Hosts. Benötigte Berechtigung: `settings:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    delete:
      tags:
      - Einstellungen
      summary: Subscription lokal entfernen.
      operationId: subCtrl_Remove
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Löscht Key, Zugangsschlüssel und Vertragsstand aus dieser Installation.
        Die Bindung des Keys beim Anbieter bleibt bestehen, bis er sie freigibt.
        Benötigte Berechtigung: `settings:manage`.'
      responses:
        '200':
          description: Entfernt; antwortet mit dem neuen Status.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/subscription/activate:
    post:
      tags:
      - Einstellungen
      summary: Subscription-Key aktivieren.
      operationId: subCtrl_Activate
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Tauscht den Subscription-Key beim Anbieter-Dienst gegen einen an diese
        Instanz gebundenen Repository-Zugangsschlüssel (die erste Instanz gewinnt; ein bereits
        anderweitig gebundener Key wird mit Klartext abgelehnt). Key und Zugangsschlüssel werden
        AES-GCM-verschlüsselt gespeichert. Benötigte Berechtigung: `settings:manage`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [subscription_key]
              properties:
                subscription_key:
                  type: string
                  description: Der vom Anbieter ausgestellte Key (LCM-E-…).
                service_url:
                  type: string
                  description: Optionale eigene Dienst-Adresse (leer = Standard).
      responses:
        '200':
          description: Aktiviert; antwortet mit dem neuen Status.
        '400':
          description: Abgelehnt (unbekannt, abgelaufen, widerrufen oder an eine andere Instanz gebunden).
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/subscription/apt:
    post:
      tags:
      - Einstellungen
      summary: LCM-Host auf einen Paketkanal umstellen (community, beta oder enterprise).
      operationId: subCtrl_SetAptChannel
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Startet einen Job auf dem selbst-registrierten LCM-Host. `enterprise`:
        Zugangsdaten in apt auth.conf.d, eigene Enterprise-Quelle, Kanaltrennung, Prüfung nur
        gegen die neue Quelle mit Rückbau bei Fehlschlag - verlangt eine aktive Subscription.
        `beta`: offene Beta-Quelle neben der Community-Quelle (kein Zugang nötig). `community`:
        der Rückweg, der nie ohne Paketquelle zurücklässt. Das frühere Feld `enable` gilt
        weiterhin, solange kein `channel` mitkommt (true = enterprise).
        Benötigte Berechtigung: `settings:manage`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                channel:
                  type: string
                  enum: [community, beta, enterprise]
                enable:
                  type: boolean
                  description: Veraltet - nur ausgewertet, wenn channel fehlt.
      responses:
        '202':
          description: Job gestartet (Job-Objekt in der Antwort).
        '400':
          description: Nicht möglich (unbekannter Kanal, keine aktive Subscription oder kein LCM-Host mit apt).
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Auf dem LCM-Host läuft bereits ein Job.
  /api/v1/subscription/verify:
    post:
      tags:
      - Einstellungen
      summary: Lebenszeichen - Vertragsstand jetzt prüfen.
      operationId: subCtrl_Verify
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Bestätigt den Zugangsschlüssel beim Anbieter-Dienst, meldet LCM-Version und
        Serverzahl (rein informativ) und übernimmt Status und Ablaufdatum. Läuft zusätzlich
        automatisch täglich. Benötigte Berechtigung: `settings:manage`.'
      responses:
        '200':
          description: Geprüft; antwortet mit dem neuen Status.
        '400':
          description: Keine Subscription hinterlegt.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/global:
    get:
      tags:
      - Einstellungen
      summary: Get Settings
      operationId: opsCtrl_GetSettings
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `settings:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    patch:
      tags:
      - Einstellungen
      summary: Update Settings
      operationId: opsCtrl_UpdateSettings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                default_ssh_user:
                  type: string
                default_ssh_password:
                  type: string
                default_ssh_port:
                  type: integer
                log_retention_days:
                  type: integer
                storage_history_retention_days:
                  type: integer
                backup_enabled:
                  type: boolean
                backup_interval_hours:
                  type: integer
                backup_retention:
                  type: integer
                backup_time:
                  type: string
                  description: >-
                    Anker-Uhrzeit des Backup-Zeitplans (HH:MM, Serverzeit).
                    Leer = 03:30.
                backup_dir:
                  type: string
                restore_auto_restart:
                  type: boolean
                cve_scan_enabled:
                  type: boolean
                cve_scan_cron:
                  type: string
                cve_high_weight_packages:
                  type: string
                session_ttl_minutes:
                  type: integer
                job_max_runtime_minutes:
                  type: integer
                apt_cache_url:
                  type: string
                require_2fa_roles:
                  type: string
                public_base_url:
                  type: string
                mail_enabled:
                  type: boolean
                mail_host:
                  type: string
                mail_port:
                  type: integer
                mail_username:
                  type: string
                mail_password:
                  type: string
                mail_from:
                  type: string
                mail_use_tls:
                  type: boolean
                mail_admin_recipients:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `settings:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/global/test-mail:
    post:
      tags:
      - Einstellungen
      summary: Verschickt eine Testnachricht an die Admin-Empfänger des System-Mailers.
      operationId: opsCtrl_TestSystemMail
      description: 'Verschickt eine Testnachricht an die Admin-Empfänger des System-Mailers.


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/ip-allowlists:
    get:
      tags:
      - Server
      summary: IP-Allowlists als Auswahl für Firewall-Regeln und Sicherheits-Werkzeuge (Lese-Sicht).
      operationId: opsCtrl_IPAllowlistPicker
      description: 'IP-Allowlists als Auswahl für Firewall-Regeln und Sicherheits-Werkzeuge.


        Benötigte Berechtigung: `servers:read`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/ip-allowlists:
    get:
      tags:
      - Einstellungen
      summary: Verwaltbare IP-Allowlists (benannte Listen von Quell-IPs/-Netzen, IPv4/IPv6 inkl. CIDR).
      operationId: opsCtrl_ListIPAllowlists
      description: 'Verwaltbare IP-Allowlists (benannte Listen von Quell-IPs/-Netzen, IPv4/IPv6 inkl. CIDR).


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Einstellungen
      summary: Legt eine Allowlist an (ohne id) oder aktualisiert sie (mit id); Einträge werden kanonisiert.
      operationId: opsCtrl_SaveIPAllowlist
      description: 'Legt eine Allowlist an (ohne id) oder aktualisiert sie (mit id); Einträge werden kanonisiert.


        Benötigte Berechtigung: `settings:manage`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: integer
                name:
                  type: string
                description:
                  type: string
                entries:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '400':
          description: Ungültige Eingabe.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/ip-allowlists/{id}:
    delete:
      tags:
      - Einstellungen
      summary: Löscht eine IP-Allowlist.
      operationId: opsCtrl_DeleteIPAllowlist
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `settings:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '404':
          description: Nicht gefunden.
  /api/v1/settings/known-repos:
    get:
      tags:
      - Einstellungen
      summary: Der pflegbare Katalog; die servers:read-Sicht für das Server-Detail liegt unter /servers/known-repos.
      operationId: opsCtrl_ListKnownRepos
      description: 'Der pflegbare Katalog; die servers:read-Sicht für das Server-Detail liegt unter /servers/known-repos.


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Einstellungen
      summary: Legt einen Katalog-Eintrag an (ohne id) oder aktualisiert ihn (mit id).
      operationId: opsCtrl_SaveKnownRepo
      description: 'Legt einen Katalog-Eintrag an (ohne id) oder aktualisiert ihn (mit id).


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/known-repos/{id}:
    delete:
      tags:
      - Einstellungen
      summary: Delete Known Repo
      operationId: opsCtrl_DeleteKnownRepo
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `settings:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/mcp:
    put:
      tags:
      - Einstellungen
      summary: Schaltet die MCP-Schnittstelle ein/aus und setzt Bind-Adresse/Port (Listener zur Laufzeit).
      operationId: opsCtrl_SetMCP
      description: 'Schaltet die MCP-Schnittstelle (read-only Serverdaten für KI-Agenten) ein/aus
        und setzt Bind-Adresse/Port; der separate Listener wird direkt gestartet/gestoppt.


        Benoetigte Berechtigung: `settings:manage`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                bind_host:
                  type: string
                port:
                  type: integer
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '400':
          description: Ungueltige Eingabe.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/settings/mail-channel:
    get:
      tags:
      - Einstellungen
      summary: Zustand der Checkbox „Standard-E-Mail-Versand als Benachrichtigungskanal".
      operationId: notificationCtrl_GetSystemChannel
      description: 'Zustand der Checkbox „Standard-E-Mail-Versand als Benachrichtigungskanal".


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    put:
      tags:
      - Einstellungen
      summary: Schaltet den verwalteten System-E-Mail-Kanal (Anlegen beim ersten Aktivieren; Abschalten
        deaktiviert nur, Alar
      operationId: notificationCtrl_SetSystemChannel
      description: 'Schaltet den verwalteten System-E-Mail-Kanal (Anlegen beim ersten Aktivieren; Abschalten
        deaktiviert nur, Alarmregeln bleiben intakt).


        Benötigte Berechtigung: `settings:manage`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/ssh-sessions/{id}:
    get:
      tags:
      - SSH-Sitzungen
      summary: Eine einzelne Session mit allen Kommandos.
      operationId: sshLogCtrl_Session
      description: 'Eine einzelne Session mit allen Kommandos.


        Benötigte Berechtigung: `jobs:read`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/backups:
    get:
      tags:
      - System
      summary: List Backups
      operationId: opsCtrl_ListBackups
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `backups:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/backups/restore-upload:
    post:
      tags:
      - System
      summary: Bereitet die Wiederherstellung aus einem HOCHGELADENEN Archiv vor.
      operationId: opsCtrl_RestoreUpload
      description: 'Bereitet die Wiederherstellung aus einem HOCHGELADENEN Archiv vor. Damit lässt sich
        ein Backup auch auf einer frischen/leeren Instanz einspielen (nach Anmeldung mit dem Admin-Konto).
        Multipart: Feld "archive" + "passphrase".


        Benötigte Berechtigung: `backups:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/backups/settings:
    patch:
      tags:
      - System
      summary: Configure Backups
      operationId: opsCtrl_ConfigureBackups
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                interval_hours:
                  type: integer
                retention:
                  type: integer
                time:
                  type: string
                  description: >-
                    Anker-Uhrzeit des Zeitplans (HH:MM, Serverzeit). Leer =
                    03:30.
                dir:
                  type: string
                auto_restart:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `backups:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/backups/trigger-now:
    post:
      tags:
      - System
      summary: Optionale Passphrase im Body; sonst greift LCM_BACKUP_PASSPHRASE.
      operationId: opsCtrl_TriggerBackup
      description: 'Optionale Passphrase im Body; sonst greift LCM_BACKUP_PASSPHRASE.


        Benötigte Berechtigung: `backups:manage`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                passphrase:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/backups/{name}:
    delete:
      tags:
      - System
      summary: Entfernt ein Backup dauerhaft (Datei + Metadaten).
      operationId: opsCtrl_DeleteBackup
      description: 'Entfernt ein Backup dauerhaft (Datei + Metadaten). Verhindert, dass die Backup-Historie
        unbegrenzt anwächst.


        Benötigte Berechtigung: `backups:manage`.'
      parameters:
      - name: name
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/backups/{name}/download:
    get:
      tags:
      - System
      summary: Liefert das verschlüsselte .lcmbak-Archiv zum Herunterladen.
      operationId: opsCtrl_DownloadBackup
      description: 'Liefert das verschlüsselte .lcmbak-Archiv zum Herunterladen.


        Benötigte Berechtigung: `backups:manage`.'
      parameters:
      - name: name
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/backups/{name}/restore:
    post:
      tags:
      - System
      summary: Bereitet die Wiederherstellung aus einem Backup der Historie vor (Rollback auf einen früheren
        Stand).
      operationId: opsCtrl_RestoreBackup
      description: 'Bereitet die Wiederherstellung aus einem Backup der Historie vor (Rollback auf einen
        früheren Stand). Passphrase im Body.


        Benötigte Berechtigung: `backups:manage`.'
      parameters:
      - name: name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                passphrase:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/info:
    get:
      tags:
      - System
      summary: Liefert Version, Build-Nummer und Laufzeitdaten der Anwendung.
      operationId: systemCtrl_Info
      description: 'Liefert Version, Build-Nummer und Laufzeitdaten der Anwendung.
        Enthält zusätzlich agent_port - den dedizierten Port des lcm-agent-
        Listeners (LCM Remote), aus dem die UI die Enroll-URL baut; 0 = kein
        dedizierter Agent-Listener aktiv.


        Öffentlicher Endpunkt (keine Authentifizierung nötig).'
      security: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/schedules/kind/{kind}/trigger-now:
    post:
      tags:
      - System
      summary: Triggert einen system-globalen Schedule (backup|cleanup) sofort.
      operationId: opsCtrl_TriggerSystemSchedule
      description: 'Triggert einen system-globalen Schedule (backup|cleanup) sofort.


        Benötigte Berechtigung: `settings:manage`.'
      parameters:
      - name: kind
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/schedules/overview:
    get:
      tags:
      - System
      summary: Schedules Overview
      operationId: opsCtrl_SchedulesOverview
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `settings:manage`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/schedules/{id}/trigger-now:
    post:
      tags:
      - System
      summary: Triggert einen Gruppen-Schedule anhand seiner ID (global, ScopeAll).
      operationId: opsCtrl_TriggerGlobalSchedule
      description: 'Triggert einen Gruppen-Schedule anhand seiner ID (global, ScopeAll).


        Benötigte Berechtigung: `settings:manage`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/self-update:
    get:
      tags:
      - System
      summary: Liefert, ob sich LCM hier selbst aktualisieren kann und wie weit eine angeforderte
        Aktualisierung ist.
      operationId: opsCtrl_SelfUpdateStatus
      description: 'Liefert, ob sich LCM hier selbst aktualisieren kann und wie weit eine angeforderte
        Aktualisierung ist.


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - System
      summary: Fordert das Selbst-Update an.
      operationId: opsCtrl_StartSelfUpdate
      description: 'Fordert das Selbst-Update an. Der Aufruf kehrt sofort zurück; LCM wartet danach,
        bis kein Job mehr läuft, und spielt das Paket dann ein.


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
        '409':
          description: Selbst-Update hier nicht möglich (Grund im Text).
  /api/v1/system/update-check:
    post:
      tags:
      - System
      summary: Löst die Update-Prüfung sofort aus (für den „Jetzt prüfen"-Button) und liefert den frischen
        Status zurück.
      operationId: opsCtrl_CheckUpdateNow
      description: 'Löst die Update-Prüfung sofort aus (für den „Jetzt prüfen"-Button) und liefert den
        frischen Status zurück.


        Benötigte Berechtigung: `settings:manage`.'
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/system/update-info:
    get:
      tags:
      - System
      summary: Liefert den zwischengespeicherten Update-Status (installiert vs.
      operationId: opsCtrl_UpdateInfo
      description: Liefert den zwischengespeicherten Update-Status (installiert vs. neueste bekannte Version)
        fürs Banner und die Footer-Anzeige.
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/users:
    get:
      tags:
      - Benutzer (LCM-Login)
      summary: List
      operationId: userCtrl_List
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `users:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    post:
      tags:
      - Benutzer (LCM-Login)
      summary: Create
      operationId: userCtrl_Create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                username:
                  type: string
                email:
                  type: string
                password:
                  type: string
                first_name:
                  type: string
                last_name:
                  type: string
                roles:
                  type: array
                  items:
                    type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `users:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/users/activation-links/consume:
    post:
      tags:
      - Benutzer (LCM-Login)
      summary: Consume Activation Link
      operationId: provCtrl_ConsumeActivationLink
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                token:
                  type: string
                password:
                  type: string
      security: []
      description: Öffentlicher Endpunkt (keine Authentifizierung nötig).
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/users/activation-links/generate:
    post:
      tags:
      - Benutzer (LCM-Login)
      summary: Generate Activation Link
      operationId: provCtrl_GenerateActivationLink
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user_id:
                  type: integer
                ttl_hours:
                  type: integer
                send_email:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `users:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/users/{id}:
    get:
      tags:
      - Benutzer (LCM-Login)
      summary: Get
      operationId: userCtrl_Get
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `users:read`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
    delete:
      tags:
      - Benutzer (LCM-Login)
      summary: Delete
      operationId: userCtrl_Delete
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `users:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/users/{id}/active:
    patch:
      tags:
      - Benutzer (LCM-Login)
      summary: Konto sperren oder entsperren.
      operationId: userCtrl_SetActive
      description: 'Sperrt bzw. entsperrt ein Konto, ohne es zu loeschen.

        

        

        Benötigte Berechtigung: `users:write`.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                active:
                  type: boolean
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/users/{id}/profile:
    patch:
      tags:
      - Benutzer (LCM-Login)
      summary: Admins (users:write) dürfen jeden bearbeiten; jeder eingeloggte User darf sein EIGENES
        Profil bearbeiten.
      operationId: userCtrl_UpdateProfile
      description: Admins (users:write) dürfen jeden bearbeiten; jeder eingeloggte User darf sein EIGENES
        Profil bearbeiten.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                first_name:
                  type: string
                last_name:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/users/{id}/reset-password:
    post:
      tags:
      - Benutzer (LCM-Login)
      summary: Admins dürfen jedes Passwort zurücksetzen; jeder User sein eigenes.
      operationId: userCtrl_ResetPassword
      description: 'Admins dürfen jedes Passwort zurücksetzen; jeder User sein eigenes. Beim eigenen Passwort
        (Self-Service) ist eine Re-Authentifizierung nötig: das AKTUELLE Passwort (und bei aktivem TOTP
        ein gültiger Code) - so kann ein gekapertes Token allein den Account nicht übernehmen.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                password:
                  type: string
                current_password:
                  type: string
                code:
                  type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.
  /api/v1/users/{id}/roles:
    put:
      tags:
      - Benutzer (LCM-Login)
      summary: Update Roles
      operationId: userCtrl_UpdateRoles
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                roles:
                  type: array
                  items:
                    type: string
      security:
      - sessionAuth: []
      - apiKeyAuth: []
      description: 'Benötigte Berechtigung: `roles:write`.'
      responses:
        '200':
          description: Erfolgreich.
        '401':
          description: Nicht angemeldet.
        '403':
          description: Keine Berechtigung.

