openapi: 3.0.3
info:
  title: Zyvor VM Services API
  version: 0.1.0
  description: Kubernetes-native VM import, inspection, migration planning, and KubeVirt provisioning.
servers:
  - url: http://api.zyvor.local/api/v1
    description: Cluster ingress
paths:
  /health:
    get:
      summary: Health check
      responses:
        '200':
          description: OK
  /vms/import:
    post:
      summary: Import VM disk image
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        '200':
          description: Image imported
  /vms:
    get:
      summary: List imported VM images
      responses:
        '200':
          description: VM list
  /vms/{id}/inspect:
    post:
      summary: Enqueue offline inspect job
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Job enqueued
  /vms/{id}/doctor:
    post:
      summary: Enqueue bootability doctor job
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
        - in: query
          name: target
          schema:
            type: string
            default: kubevirt
        - in: query
          name: explain
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: Job enqueued
  /vms/{id}/migration-plan:
    post:
      summary: Enqueue migration plan job
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
        - in: query
          name: target
          schema:
            type: string
            default: kubevirt
      responses:
        '200':
          description: Job enqueued
  /vms/{id}/repair-plan:
    post:
      summary: Enqueue repair plan job (dry-run default; set dry_run=false to apply)
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
        - in: query
          name: dry_run
          schema:
            type: boolean
            default: true
        - in: query
          name: fix
          schema:
            type: string
            default: boot
      responses:
        '200':
          description: Job enqueued
  /vms/{id}/profile:
    post:
      summary: Enqueue security/compliance profile job
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                profiles:
                  type: array
                  items:
                    type: string
                  default: [security, compliance, hardening, migration]
      responses:
        '200':
          description: Job enqueued
  /vms/{id}/explore:
    post:
      summary: Enqueue read-only guest filesystem explore (ls|stat|cat)
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum: [ls, stat, cat]
                  default: ls
                path:
                  type: string
                  default: /
      responses:
        '200':
          description: Job enqueued
  /vms/{id}/provision:
    post:
      summary: Generate KubeVirt VM + DataVolume YAML
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
        - in: query
          name: apply
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: YAML manifests returned
  /jobs/{id}:
    get:
      summary: Get job status and result
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Job details
  /config:
    get:
      summary: UI configuration (Zeus URL, cluster name, storage defaults)
      responses:
        '200':
          description: UiConfig
  /vmtools/bundle:
    get:
      summary: Zeus VM Tools artifact URLs and version
      responses:
        '200':
          description: VMToolsBundleInfo
  /vmtools/coverage:
    get:
      summary: Fleet-wide VM Tools install/connection coverage
      responses:
        '200':
          description: VMToolsCoverage
  /vmtools/policy:
    get:
      summary: Cluster VM Tools policy (VMToolsPolicy CR)
      responses:
        '200':
          description: VMToolsPolicyView
    put:
      summary: Upsert cluster VM Tools policy
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                spec:
                  type: object
                  properties:
                    selector:
                      type: object
                    autoInstall:
                      type: boolean
                    autoUpgrade:
                      type: boolean
                    channel:
                      type: string
                    rebootPolicy:
                      type: string
                    maxConcurrent:
                      type: integer
                      minimum: 1
      responses:
        '200':
          description: VMToolsPolicyView
  /vmtools/policy/reconcile:
    post:
      summary: Reconcile fleet against policy (auto-install / auto-upgrade)
      responses:
        '200':
          description: VMToolsReconcileResult
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VMToolsReconcileResult'
  /storage/roots:
    get:
      summary: Server disk browse roots
      responses:
        '200':
          description: Storage roots
  /storage/browse:
    get:
      summary: Browse directories and disk images on server storage
      responses:
        '200':
          description: Storage browse listing
  /vms/import-from-storage:
    post:
      summary: Register a disk from server storage into Zyvor ingest
      responses:
        '200':
          description: Import result
  /kubevirt/namespaces:
    get:
      summary: Distinct namespaces containing KubeVirt VMs
      responses:
        '200':
          description: Namespace list
  /kubevirt/vms:
    get:
      summary: List KubeVirt VMs in cluster
      parameters:
        - in: query
          name: namespace
          schema:
            type: string
        - in: query
          name: search
          schema:
            type: string
        - in: query
          name: phase
          schema:
            type: string
            enum: [Running, Stopped, Pending]
      responses:
        '200':
          description: VM summaries
  /kubevirt/vms/{namespace}/{name}/guest-agent:
    get:
      summary: Live guest agent status for a cluster VM
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: GuestAgentInfo
  /kubevirt/vms/{namespace}/{name}/guest/status:
    get:
      summary: Guest control state, transport ladder probes, and capability contract
      responses:
        '200':
          description: GuestControlEnvelope
  /kubevirt/vms/{namespace}/{name}/guest/capabilities:
    get:
      summary: Negotiated guest capability contract
      responses:
        '200':
          description: GuestControlEnvelope
  /kubevirt/vms/{namespace}/{name}/guest/doctor:
    get:
      summary: Agent Doctor probe tree and recommendations
      responses:
        '200':
          description: GuestControlEnvelope with AgentDoctorReport in data
    post:
      summary: Run live guestkit.doctor via best transport
      responses:
        '200':
          description: GuestControlEnvelope with live doctor result
  /kubevirt/vms/{namespace}/{name}/guest/readiness:
    get:
      summary: Guest readiness score (0-100) for migration preflight
      responses:
        '200':
          description: GuestControlEnvelope
  /kubevirt/vms/{namespace}/{name}/guest/install-agent:
    post:
      summary: Strategy-aware agent install (cloud-init, QGA curl, QGA file bootstrap)
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                strategy:
                  type: string
                  enum: [auto, qga_file_bootstrap, qga_curl_bootstrap, cloud_init_curl, offline_inject, iso_attach]
                restart:
                  type: boolean
                bundleUrl:
                  type: string
      responses:
        '200':
          description: GuestControlEnvelope
  /kubevirt/vms/{namespace}/{name}/guest/repair-plan:
    post:
      summary: Offline repair plan for halted VM (inject agent, QGA, cloud-init fixes)
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                dryRun:
                  type: boolean
                injectQga:
                  type: boolean
                injectZyvorAgent:
                  type: boolean
                enableSystemd:
                  type: boolean
                fixCloudInitNetwork:
                  type: boolean
                validateFstab:
                  type: boolean
      responses:
        '200':
          description: GuestControlEnvelope with repair job enqueue result
  /kubevirt/vms/{namespace}/{name}/guest/file/read:
    post:
      summary: Read guest file via QGA guest-file API
      responses:
        '200':
          description: GuestControlEnvelope with base64 content
  /kubevirt/vms/{namespace}/{name}/guest/file/write:
    post:
      summary: Write guest file via QGA guest-file API (airgap installer)
      responses:
        '200':
          description: GuestControlEnvelope
  /kubevirt/guest/poll-reconcile:
    post:
      summary: Host-mediated poll reconcile for AirgapLive VMs without push
      responses:
        '200':
          description: Poll summary
  /kubevirt/vms/{namespace}/{name}/vmtools:
    get:
      summary: Zeus VM Tools status for a cluster VM
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: VMToolsVmStatus
  /kubevirt/vms/{namespace}/{name}/vmtools/install:
    post:
      summary: Install Zeus VM Tools (cloud-init or QGA bootstrap script)
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
        - in: query
          name: restart
          schema:
            type: boolean
      responses:
        '200':
          description: GuestAgentInstallResult
  /kubevirt/vms/{namespace}/{name}/vmtools/diagnostics:
    post:
      summary: Run VM Tools diagnostics and sync labels
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Diagnostics result
  /kubevirt/vms/{namespace}/{name}/vmtools/quiesce:
    post:
      summary: Quiesce guest filesystem for snapshot (KubeVirt freeze)
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: VMToolsOpResult
  /kubevirt/vms/{namespace}/{name}/vmtools/unquiesce:
    post:
      summary: Thaw guest filesystem after snapshot (KubeVirt unfreeze)
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: VMToolsOpResult
  /kubevirt/vms/{namespace}/{name}/vmtools/reboot:
    post:
      summary: Guest soft reboot via QEMU guest agent
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: VMToolsOpResult
  /kubevirt/vms/{namespace}/{name}/vmtools/shutdown:
    post:
      summary: Graceful VM shutdown with configurable grace period
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                grace_period_seconds:
                  type: integer
      responses:
        '200':
          description: VMToolsOpResult
  /kubevirt/vms/{namespace}/{name}/vmtools/exec:
    post:
      summary: Guest command exec (not available via KubeVirt API)
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '400':
          description: Not supported — use SSH or agent RPC
  /kubevirt/vms/{namespace}/{name}/start:
    put:
      summary: Start a stopped KubeVirt VM
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: LifecycleResult
  /kubevirt/vms/{namespace}/{name}/stop:
    put:
      summary: Gracefully stop a KubeVirt VM
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: LifecycleResult
  /kubevirt/vms/{namespace}/{name}/restart:
    put:
      summary: Restart a KubeVirt VM
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: LifecycleResult
  /kubevirt/vms/{namespace}/{name}/export-disk:
    post:
      summary: Export cluster VM root PVC into Zyvor ingest storage
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
        - in: query
          name: force_stop
          schema:
            type: boolean
      responses:
        '200':
          description: ExportDiskResult
        '409':
          description: VM running — stop first
  /kubevirt/vms/{namespace}/{name}/copilot/briefing:
    post:
      summary: Cluster Copilot briefing from guest-agent and boot-inspect evidence
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClusterCopilotInput'
      responses:
        '200':
          description: MigrationBriefing
  /kubevirt/vms/{namespace}/{name}/copilot/ask:
    post:
      summary: Ask Cluster Copilot a question against a briefing
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClusterAskBody'
      responses:
        '200':
          description: CopilotInsight
  /kubevirt/apply:
    post:
      summary: Apply raw KubeVirt YAML manifests to the cluster
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApplyYamlRequest'
      responses:
        '200':
          description: ApplyYamlResult
  /kubevirt/vms/{namespace}/{name}/boot-inspect:
    get:
      summary: Offline boot inspect (GuestKit run_boot_inspect — not legacy appliance tooling)
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      responses:
        '200':
          description: BootInspectInfo
    post:
      summary: Offline boot inspect (optional PVC override in body)
      parameters:
        - in: path
          name: namespace
          required: true
          schema:
            type: string
        - in: path
          name: name
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BootInspectRequest'
      responses:
        '200':
          description: BootInspectInfo
  /kubevirt/boot-inspect:
    post:
      summary: Offline boot inspect by namespace/vm body
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BootInspectRequest'
      responses:
        '200':
          description: BootInspectInfo
  /system/status:
    get:
      summary: Zeus API and cluster integration status
      responses:
        '200':
          description: SystemStatus
  /guest-actions/pending:
    get:
      summary: List pending guest remediation actions (operator/admin when auth enabled)
      responses:
        '200':
          description: PendingGuestAction list
  /guest-actions/audit:
    get:
      summary: Recent guest remediation audit history
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        '200':
          description: PendingGuestAction audit list
  /guest-actions/{id}/approve:
    post:
      summary: Approve and execute a pending guest remediation action
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Action approved
  /guest-actions/{id}/reject:
    post:
      summary: Reject a pending guest remediation action
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Action rejected
  /packetwolf/fleet-snapshot:
    get:
      summary: Last PacketWolf fleet correlation snapshot from Redis
      responses:
        '200':
          description: Fleet correlation snapshot
  /packetwolf/fleet-correlate:
    post:
      summary: Trigger PacketWolf fleet correlation sweep (operator/admin when auth enabled)
      responses:
        '200':
          description: Fleet correlation result
  /guest-agents/register:
    post:
      summary: Register a Zyvor guest agent (push transport)
      responses:
        '200':
          description: Registration result
  /guest-agents/bootstrap-info:
    get:
      summary: Guest agent bootstrap CA and policy hints
      responses:
        '200':
          description: Bootstrap info
  /guest-agents/bootstrap:
    post:
      summary: Issue guest agent mTLS certificate
      responses:
        '200':
          description: Bootstrap certificate
  /guest-agents/{agent_id}/heartbeat:
    post:
      summary: Guest agent heartbeat (push transport)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Heartbeat acknowledged
  /guest-agents/{agent_id}/report:
    get:
      summary: Fetch last guest agent report for agent id
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Guest agent report
    post:
      summary: Guest agent evidence push (health, metrics, journal)
      parameters:
        - in: path
          name: agent_id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Report accepted
components:
  schemas:
    BootInspectRequest:
      type: object
      properties:
        namespace:
          type: string
        vm:
          type: string
        pvc:
          type: string
        mode:
          type: string
          example: boot-inspect
        source:
          type: string
          example: zeus-os
    BootInspectInfo:
      type: object
      properties:
        available:
          type: boolean
        source:
          type: string
          example: guestkit
        os_release:
          type: string
        fstab_valid:
          type: boolean
        bootloader:
          type: string
        cloud_init_present:
          type: boolean
        message:
          type: string
    ClusterCopilotInput:
      type: object
      properties:
        guest_agent:
          type: object
        boot_inspect:
          $ref: '#/components/schemas/BootInspectInfo'
    ClusterAskBody:
      type: object
      required: [question]
      properties:
        question:
          type: string
        briefing:
          type: object
    ApplyYamlRequest:
      type: object
      required: [yaml]
      properties:
        yaml:
          type: string
    ApplyYamlResult:
      type: object
      properties:
        applied:
          type: boolean
        resources:
          type: array
          items:
            type: object
        errors:
          type: array
          items:
            type: string
    VMToolsCoverage:
      type: object
      properties:
        total_vms:
          type: integer
        installed:
          type: integer
        connected:
          type: integer
        pending:
          type: integer
        missing:
          type: integer
        outdated:
          type: integer
        windows_virtio_win:
          type: integer
    VMToolsBundleInfo:
      type: object
      properties:
        version:
          type: string
        channel:
          type: string
        linux_rpm_url:
          type: string
          nullable: true
        linux_deb_url:
          type: string
          nullable: true
        linux_tar_url:
          type: string
          nullable: true
        windows_exe_url:
          type: string
          nullable: true
        windows_msi_url:
          type: string
          nullable: true
        windows_zip_url:
          type: string
          nullable: true
        windows_install_ps1_url:
          type: string
          nullable: true
        linux_tar_sha256:
          type: string
          nullable: true
        linux_tar_signature:
          type: string
          nullable: true
        windows_zip_sha256:
          type: string
          nullable: true
        windows_zip_signature:
          type: string
          nullable: true
        iso_url:
          type: string
          nullable: true
        agent_binary_url:
          type: string
    VMToolsReconcileResult:
      type: object
      properties:
        policy:
          type: string
        scanned:
          type: integer
        matched:
          type: integer
        installed:
          type: integer
        pending:
          type: integer
        upgraded:
          type: integer
        skipped:
          type: integer
        errors:
          type: array
          items:
            type: string
    SystemStatus:
      type: object
      properties:
        agent:
          type: string
        cluster:
          type: string
        storage:
          type: string
        kubevirt:
          type: string
        cdi:
          type: string
        last_scan:
          type: string
          nullable: true
        disk_count:
          type: integer
        cluster_vm_count:
          type: integer
          nullable: true
        worker:
          type: string
        guest_agent_mtls:
          type: boolean
        packetwolf_correlation:
          type: boolean
        packetwolf_fleet:
          type: boolean
    PendingGuestAction:
      type: object
      properties:
        id:
          type: string
        action:
          type: string
        namespace:
          type: string
        vm_name:
          type: string
        unit:
          type: string
          nullable: true
        status:
          type: string
          enum: [pending, approved, rejected]
        created_at:
          type: string
          format: date-time
        requested_by:
          type: string
          nullable: true
        approved_at:
          type: string
          format: date-time
          nullable: true
        approved_by:
          type: string
          nullable: true
        rejected_at:
          type: string
          format: date-time
          nullable: true
        rejected_by:
          type: string
          nullable: true
        result:
          type: object
          nullable: true
