{
  "components": {
    "schemas": {
      "AddressMode": {
        "description": "How an interface's address is configured.\n\nRead from the host's persistent configuration, not from the live\naddress — an interface that currently *has* a static-looking address\nbecause a DHCP lease happens to be stable is still `dhcp`, and treating\nit as static is exactly the mistake `OVN.md` decision 4 exists to\nprevent.",
        "enum": [
          "dhcp",
          "static",
          "manual",
          "loopback",
          "unconfigured"
        ],
        "type": "string"
      },
      "CrashSafety": {
        "description": "How a watchdog keeps counting when the process holding it dies.\n\nLinux stops a watchdog on close *only* if the magic character `V` was\nwritten first. A crash closes the descriptor with nothing written, so the\ntimer survives — which is precisely what a fence needs. A driver offering\nneither of these is unusable as a fence, which is why the absence of both is\nmodelled as `None` rather than a third variant.",
        "enum": [
          "magic_close",
          "no_way_out"
        ],
        "type": "string"
      },
      "DatastoreStats": {
        "description": "Response of `GET /api/v1/datastores/stats`.",
        "properties": {
          "available_bytes": {
            "description": "Free space.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "capacity_bytes": {
            "description": "Total capacity.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "kind": {
            "$ref": "#/components/schemas/HostDatastoreKind",
            "description": "Storage backend."
          },
          "path": {
            "description": "ZFS dataset name or absolute directory path.",
            "type": "string"
          }
        },
        "required": [
          "kind",
          "path",
          "capacity_bytes",
          "available_bytes"
        ],
        "type": "object"
      },
      "EnrollCsrResponse": {
        "description": "Response of `GET /api/v1/enroll/csr` (`MTLS.md` slice 3).\n\nThe agent generates a keypair the first time this is called and persists\nit before answering — never in the response, and never anywhere but this\nhost's own disk (`MTLS.md` §2: the private key never leaves the machine it\nbelongs to). A second call against the same unenrolled host returns a\nrequest for the *same* key rather than generating a new one, so a retried\ncontrol-plane call cannot leave a host holding a key nothing will ever sign.",
        "properties": {
          "csr_pem": {
            "description": "PEM-encoded PKCS#10 certificate signing request.",
            "type": "string"
          }
        },
        "required": [
          "csr_pem"
        ],
        "type": "object"
      },
      "FenceKind": {
        "description": "How a host can be forced into a state where its VMs are provably not\nrunning.\n\nOne variant today. Out-of-band power fencing is the planned second\n(`HA-RESTART.md` §8, slice B); the enum exists now so the control plane\nrecords *which* fence a host proved rather than a bare boolean, which is\ncheap now and expensive to retrofit.",
        "enum": [
          "watchdog"
        ],
        "type": "string"
      },
      "FencingInfo": {
        "description": "A host's fencing capability, as the agent observes it.\n\nEvery field is read from `/sys/class/watchdog/<dev>/`, never from an\n`ioctl` — the workspace denies `unsafe_code`, and sysfs exposes everything\n`WDIOC_GETSUPPORT` would return (`HA-RESTART.md` §6.1a).",
        "properties": {
          "armed": {
            "description": "Whether this *machine's* fence is running — not merely whether this\nagent process holds the descriptor.\n\nThe distinction matters for a moment after an agent restart, when the\ntimer a previous process started is still counting with nobody holding\nit. The control plane's question is \"is this host fenced\", and during\nthat window the honest answer is yes, so this reports the device's own\nstate as well as the agent's. The agent adopts such an orphan at\nstartup, which closes the window rather than papering over it.",
            "type": "boolean"
          },
          "crash_safety": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/CrashSafety",
                "description": "By which mechanism a crash of the agent still resets the machine, or\n`None` if it would not — in which case this device cannot be armed.\n\nReported rather than assumed, because it is the property the whole\nfence rests on: a device that silently disarms when the agent dies is\nworse than no device, since HA restart would trust it."
              }
            ]
          },
          "device": {
            "description": "Device node the agent holds, e.g. `/dev/watchdog0`.",
            "type": "string"
          },
          "identity": {
            "description": "Driver's self-reported name, e.g. `i6300ESB timer`. Operator-facing\nonly; nothing branches on it.",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/FenceKind",
            "description": "Which mechanism this is."
          },
          "last_boot_was_fence": {
            "description": "Whether this machine's *last boot* was caused by the watchdog firing\n(`WDIOF_CARDRESET` in `bootstatus`).\n\nPositive confirmation that a fence actually fired, rather than the\ncontrol plane inferring it from how long the host was silent.",
            "type": "boolean"
          },
          "timeout_secs": {
            "description": "Seconds the machine survives without a pet. Set when the driver module\nis loaded (`modprobe i6300esb heartbeat=120`) and read back here — the\nagent reports the timeout it *has* rather than setting one, since\n`WDIOC_SETTIMEOUT` would need an `ioctl`.",
            "format": "int32",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "kind",
          "device",
          "identity",
          "timeout_secs",
          "armed",
          "last_boot_was_fence"
        ],
        "type": "object"
      },
      "HostAttachIsoRequest": {
        "description": "Body of `POST /api/v1/vms/{id}/actions/attach-iso` (M2, contract §4.9):\nthe control plane has already resolved the image to an absolute host\npath — the agent has no concept of the inventory model at all, the same\ndivision of labor as `source_image_path` elsewhere in this crate.",
        "properties": {
          "path": {
            "description": "Absolute host path of the image file to attach, read-only.",
            "type": "string"
          }
        },
        "required": [
          "path"
        ],
        "type": "object"
      },
      "HostBackupDisk": {
        "description": "One disk captured by a backup run.",
        "properties": {
          "chunks_reused": {
            "description": "Chunks the volume already held, so this run skipped — the dedup. A\nsecond backup of an unchanged disk reports all chunks here and zero in\n[`Self::chunks_written`].",
            "minimum": 0,
            "type": "integer"
          },
          "chunks_written": {
            "description": "Chunks this run actually uploaded.",
            "minimum": 0,
            "type": "integer"
          },
          "manifest_id": {
            "description": "The content-addressed manifest id (`m-…`) the run stored — the id a\nrestore resolves the disk back through.",
            "type": "string"
          },
          "name": {
            "description": "Disk name, as [`HostDiskSpec::name`] spells it.",
            "type": "string"
          },
          "sha256": {
            "description": "The disk's whole-file SHA-256, lowercase hex — what a restore verifies\nthe reassembled bytes against.",
            "type": "string"
          },
          "size_bytes": {
            "description": "The disk's byte count, as the manifest records it.",
            "format": "int64",
            "type": "integer"
          },
          "volume_path": {
            "description": "Volume path the disk's content is bound to, e.g.\n`vms/<vm_id>/backups/<backup_id>/<disk_name>.raw`.",
            "type": "string"
          }
        },
        "required": [
          "name",
          "volume_path",
          "manifest_id",
          "size_bytes",
          "sha256",
          "chunks_written",
          "chunks_reused"
        ],
        "type": "object"
      },
      "HostBackupList": {
        "description": "Response of `GET /api/v1/vms/{id}/backups`: the agent-local catalog, most\nrecent last.",
        "properties": {
          "backups": {
            "items": {
              "$ref": "#/components/schemas/HostBackupRecord"
            },
            "type": "array"
          }
        },
        "required": [
          "backups"
        ],
        "type": "object"
      },
      "HostBackupRecord": {
        "description": "What one backup run produced, and what the agent persists as its own\nbackup catalog — one JSON file per run under the agent's state root.",
        "properties": {
          "backup_id": {
            "description": "The run's id; names the catalog file and the volume subdirectory.",
            "format": "uuid",
            "type": "string"
          },
          "created_at_unix": {
            "description": "Unix seconds, UTC — when the run captured the disks.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "disks": {
            "description": "Every disk, in spec order.",
            "items": {
              "$ref": "#/components/schemas/HostBackupDisk"
            },
            "type": "array"
          },
          "location": {
            "description": "The volume location the run wrote to, exactly as resolved (request\noverride or agent configuration).",
            "type": "string"
          },
          "paused": {
            "description": "Whether the VM was paused for the capture. A running VM is paused\nfirst and resumed after (crash-consistent, the same class of image a\npower cut would leave); `false` means the VM was already stopped.",
            "type": "boolean"
          },
          "vm_id": {
            "description": "The VM this record belongs to.",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "backup_id",
          "vm_id",
          "created_at_unix",
          "location",
          "paused",
          "disks"
        ],
        "type": "object"
      },
      "HostBackupRequest": {
        "description": "Body of `POST /api/v1/vms/{id}/actions/backup`: stream every disk of a VM\ninto a content-addressed chunk-store volume through the `nfd` sidecar.\n\nThe volume layer is `NuFinder`'s (`design/volumes.md` \"Two layouts per\nfolder\"): each disk becomes one block-layout path whose manifest is\ncontent-addressed, so a second backup of the same disk re-uploads only the\nchunks that changed — incremental forever, with no fulls ever again.",
        "properties": {
          "location": {
            "description": "Volume location to back up into (an `nfd` URI: `s3://bucket/prefix`\nor a local directory). When absent, the agent uses its own\n`NEXTVIRT_BACKUP_LOCATION` configuration — the one-operator-per-host\ncase, where the volume is a property of the host rather than of any\none VM.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "HostBootSpec": {
        "description": "How the agent starts the guest.",
        "oneOf": [
          {
            "description": "Direct kernel boot from files on the host.",
            "properties": {
              "cmdline": {
                "description": "Kernel command line.",
                "type": "string"
              },
              "initramfs_path": {
                "description": "Absolute path of the initramfs, if any.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "kernel_path": {
                "description": "Absolute path of the kernel image.",
                "type": "string"
              },
              "kind": {
                "enum": [
                  "kernel"
                ],
                "type": "string"
              }
            },
            "required": [
              "kernel_path",
              "cmdline",
              "kind"
            ],
            "type": "object"
          },
          {
            "description": "UEFI firmware boot from the boot disk.",
            "properties": {
              "kind": {
                "enum": [
                  "firmware"
                ],
                "type": "string"
              }
            },
            "required": [
              "kind"
            ],
            "type": "object"
          }
        ]
      },
      "HostDatastoreKind": {
        "description": "Storage backend of a host datastore.",
        "enum": [
          "zfs",
          "file",
          "nfs",
          "iscsi",
          "volume"
        ],
        "type": "string"
      },
      "HostDatastoreRef": {
        "description": "Where a disk or image lives on the host.",
        "properties": {
          "datastore_id": {
            "description": "The datastore's own id — only meaningful for `nfs`, whose actual\non-host root is [`nfs_mountpoint`] of this, not `path` (the export\nsource is not a local path at all). Carried for every kind so this\ntype does not need an `Option` that is meaningless everywhere but one\nvariant.",
            "format": "uuid",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/HostDatastoreKind",
            "description": "Storage backend."
          },
          "path": {
            "description": "ZFS dataset name, absolute directory path, or (`nfs`) export source.",
            "type": "string"
          }
        },
        "required": [
          "kind",
          "path",
          "datastore_id"
        ],
        "type": "object"
      },
      "HostDiskSpec": {
        "description": "One virtual disk, in host terms.",
        "properties": {
          "boot": {
            "description": "Whether this is the boot disk.",
            "type": "boolean"
          },
          "datastore": {
            "$ref": "#/components/schemas/HostDatastoreRef",
            "description": "Datastore the disk is created in."
          },
          "name": {
            "description": "Disk name, unique within the VM.",
            "type": "string"
          },
          "size_bytes": {
            "description": "Provisioned size.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "source_disk_path": {
            "description": "Absolute path of another VM's live disk (same datastore kind) to\nclone this disk's initial content from (M2, VM clone — contract\n§4.9's `clone` action).\n\nReal per backend: `zfs` snapshots the source zvol and `zfs clone`s\nit — instant and thin, never destroyed while this disk exists (see\n`backends/zfs.rs`); `file`/`nfs` reflink-copy it, same mechanism as\n`source_image_path`. Mutually exclusive with `source_image_path`.",
            "type": [
              "string",
              "null"
            ]
          },
          "source_image_path": {
            "description": "Absolute path of a raw image to copy into the disk before first boot.\n\nMutually exclusive with `source_disk_path` (the control plane never\nsets both): an image copy is always a real full byte copy, since M1's\n`Image` is a plain file with no snapshot to clone (`backends/zfs.rs`'s\nmodule doc).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name",
          "datastore",
          "size_bytes",
          "boot"
        ],
        "type": "object"
      },
      "HostGcReport": {
        "description": "Body of `GET /api/v1/backups/gc`'s report: `nfd gc --json`'s shape,\nmirrored the same way the sidecar's put/get outcomes are.",
        "properties": {
          "applied": {
            "description": "Whether deletion actually happened (a dry run reports counts only).",
            "type": "boolean"
          },
          "dead_bytes": {
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "dead_chunks": {
            "minimum": 0,
            "type": "integer"
          },
          "dead_manifests": {
            "minimum": 0,
            "type": "integer"
          },
          "protected_objects": {
            "minimum": 0,
            "type": "integer"
          },
          "ran": {
            "description": "`false` when another maintenance pass held the volume's lease.",
            "type": "boolean"
          }
        },
        "required": [
          "ran",
          "applied",
          "dead_manifests",
          "dead_chunks",
          "dead_bytes",
          "protected_objects"
        ],
        "type": "object"
      },
      "HostGcRequest": {
        "description": "Body of `POST /api/v1/backups/gc`: whether to actually delete.",
        "properties": {
          "apply": {
            "description": "`false` lists what would be reclaimed — the sidecar's own dry-run\ndefault, so a curious operator costs nothing.",
            "type": "boolean"
          }
        },
        "required": [
          "apply"
        ],
        "type": "object"
      },
      "HostHealth": {
        "description": "Response of `GET /health`.",
        "properties": {
          "status": {
            "description": "Always `\"ok\"` when the agent answers at all.",
            "type": "string"
          },
          "version": {
            "description": "Agent version.",
            "type": "string"
          }
        },
        "required": [
          "status",
          "version"
        ],
        "type": "object"
      },
      "HostImportImageRequest": {
        "description": "Body of `POST /api/v1/images/import` (M2, contract §4.8): fetch `url`\ndirectly (not routed through the control plane) into `datastore`, at\n`dest_path` relative to its root, verifying `sha256` when given.",
        "properties": {
          "datastore": {
            "$ref": "#/components/schemas/HostDatastoreRef",
            "description": "Datastore to download into."
          },
          "dest_path": {
            "description": "Destination path relative to the datastore's root.",
            "type": "string"
          },
          "sha256": {
            "description": "Expected SHA-256, hex-encoded; checked once the download completes.",
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "description": "Source URL.",
            "type": "string"
          }
        },
        "required": [
          "datastore",
          "dest_path",
          "url"
        ],
        "type": "object"
      },
      "HostImportImageResponse": {
        "description": "Response of `POST /api/v1/images/import` and `PUT /api/v1/images/upload`.",
        "properties": {
          "size_bytes": {
            "description": "The downloaded (or uploaded) file's real size.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "size_bytes"
        ],
        "type": "object"
      },
      "HostInfo": {
        "description": "Response of `GET /api/v1/info`.",
        "properties": {
          "agent_instance_id": {
            "description": "Identity of this agent *process*, minted fresh at every start (M3,\narchitecture §5.10).\n\n`None` from an agent predating this field — which is exactly the agent\nthe first orchestrated upgrade targets, so a `None` → `Some(_)`\ntransition is itself a conclusive restart signal. A random id rather\nthan a start timestamp: this is only ever compared for equality, and an\nopaque value cannot be misread as uptime or be wrong under clock skew.\nA change here proves the agent *process* restarted; it proves nothing\nabout which binary it restarted into.",
            "format": "uuid",
            "type": [
              "string",
              "null"
            ]
          },
          "agent_version": {
            "description": "Agent version.",
            "type": "string"
          },
          "cpus": {
            "description": "Logical CPUs.",
            "format": "int32",
            "minimum": 0,
            "type": "integer"
          },
          "fencing": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/FencingInfo",
                "description": "The fencing hardware this host has, and whether it is armed\n(`HA-RESTART.md`, M3). `None` when the host has no usable fence, which\nis the safe default: the control plane must never restart this host's\nVMs elsewhere, because it cannot prove they stopped."
              }
            ]
          },
          "hostname": {
            "description": "Kernel hostname.",
            "type": "string"
          },
          "interfaces": {
            "description": "This host's network interfaces as the agent sees them (M4,\n`OVN.md` §8).\n\nReported on every poll rather than fetched separately, the same way\n[`HostInfo::fencing`] is: it is small, it changes rarely, and the\ncontrol plane needs it at exactly the moments a host may be\nunreachable — choosing where to put `ovn-central`, for one, which may\nnot be a DHCP-addressed node.",
            "items": {
              "$ref": "#/components/schemas/HostInterface"
            },
            "type": "array"
          },
          "kvm_available": {
            "description": "Whether `/dev/kvm` is present and usable.",
            "type": "boolean"
          },
          "memory_available_bytes": {
            "description": "Memory the kernel reports as available.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "memory_bytes": {
            "description": "Total physical memory.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "tls_port": {
            "description": "Port the agent's mTLS listener is actually bound to, when it is\nrunning (`MTLS.md` slice 2).\n\n`None` before enrollment — no listener yet — or from an agent\npredating this field, the same convention [`Self::agent_instance_id`]\nuses. The mTLS listener is a genuinely separate `TcpListener` on its\nown port (`NEXTVIRT_HOSTD_TLS_BIND`, default `8091`), independent of\nthe plain one this response was fetched over (`NEXTVIRT_HOSTD_BIND`,\ndefault `8090`) — nothing else in this response can be used to derive\nit, so the control plane must learn it here before it can ever open\nan mTLS connection to this agent.",
            "format": "int32",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "vm_count": {
            "description": "VMs the agent currently manages.",
            "format": "int32",
            "minimum": 0,
            "type": "integer"
          },
          "vmm_version": {
            "description": "Cloud Hypervisor version, when the binary is present.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "agent_version",
          "hostname",
          "cpus",
          "memory_bytes",
          "memory_available_bytes",
          "kvm_available",
          "vm_count"
        ],
        "type": "object"
      },
      "HostInterface": {
        "description": "One network interface on a host, as the agent observes it.",
        "properties": {
          "addresses": {
            "description": "Live addresses in CIDR form, e.g. `10.0.250.121/24`. Plural because an\ninterface may legitimately carry several, and reporting only the first\nwould quietly hide the rest.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "bridge": {
            "description": "Bridge this interface is a port of, when it is one.",
            "type": [
              "string",
              "null"
            ]
          },
          "gateway": {
            "description": "Default gateway configured for this interface, when it has one.",
            "type": [
              "string",
              "null"
            ]
          },
          "mac": {
            "description": "Hardware address, when the interface has one.",
            "type": [
              "string",
              "null"
            ]
          },
          "mode": {
            "$ref": "#/components/schemas/AddressMode",
            "description": "How its address is configured, from the persistent configuration."
          },
          "name": {
            "description": "Kernel name, e.g. `eth0` or `br0`.",
            "type": "string"
          },
          "up": {
            "description": "Whether the kernel currently reports the link as up.",
            "type": "boolean"
          }
        },
        "required": [
          "name",
          "mode",
          "up"
        ],
        "type": "object"
      },
      "HostInterfaceChange": {
        "description": "What the agent returns after accepting an interface change, before applying\nit.\n\nThe change is **not** confirmed by this response. It will be reverted\nautomatically unless [`Self::token`] is presented back before\n[`Self::rollback_at_unix`] — see `OVN.md` §8 for why an interface edit is\nthe one operation in this product that can strand a host with no in-band\nrecovery.",
        "properties": {
          "expected_addresses": {
            "description": "Addresses the interface is expected to have once applied, so the caller\nknows where to go looking to confirm.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "rollback_at_unix": {
            "description": "Unix seconds after which the agent restores the previous configuration\non its own.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "token": {
            "description": "Presented to `POST /api/v1/interfaces/confirm` to keep the change.",
            "type": "string"
          }
        },
        "required": [
          "token",
          "rollback_at_unix"
        ],
        "type": "object"
      },
      "HostInterfaceConfirmRequest": {
        "description": "Body of `POST /api/v1/interfaces/confirm`.\n\nShared between the agent (`nextvirt-hostd`) and the control plane\n(`nextvirt-host-client`) rather than duplicated, the same reason every\nother host request/response type lives here instead of in either crate.",
        "properties": {
          "token": {
            "description": "Token from [`HostInterfaceChange::token`].",
            "type": "string"
          }
        },
        "required": [
          "token"
        ],
        "type": "object"
      },
      "HostInterfaceUpdate": {
        "description": "A requested change to one interface's persistent configuration\n(`OVN.md` §8).",
        "properties": {
          "address": {
            "description": "Address in CIDR form, e.g. `10.0.250.50/24`. Required for `static`,\nrejected for `dhcp`.",
            "type": [
              "string",
              "null"
            ]
          },
          "gateway": {
            "description": "Default gateway. Optional even for `static` — a host on a flat\nmanagement segment may legitimately have none.",
            "type": [
              "string",
              "null"
            ]
          },
          "mode": {
            "$ref": "#/components/schemas/AddressMode",
            "description": "Mode to switch to. Only `dhcp` and `static` are settable; `manual`,\n`loopback` and `unconfigured` describe an interface rather than\nconfigure one."
          }
        },
        "required": [
          "mode"
        ],
        "type": "object"
      },
      "HostNicSpec": {
        "description": "One virtual network interface, in host terms.",
        "properties": {
          "bridge": {
            "description": "Bridge interface the tap attaches to.",
            "type": "string"
          },
          "mac": {
            "description": "MAC address of the guest interface.",
            "type": "string"
          },
          "mtu": {
            "description": "MTU to set on the guest-facing tap, when the network needs one below\nthe host's.\n\nNot cosmetic. Slice 1 measured this on real hardware: an OVN port\ninherits 1500 while the Geneve path carries ~1442, so payloads at and\nabove ~1420 are lost **silently** with DF set while small ones work\nperfectly. Setting it turns a blackhole into a local refusal the guest\ncan see.",
            "format": "int32",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "description": "Interface name, unique within the VM.",
            "type": "string"
          },
          "ovn_port": {
            "description": "OVN logical switch port this tap binds to, when the network is an OVN\none (M4, `OVN.md` slice 4).\n\n**Sent rather than derived, and that is the point.** `OVN.md` §4.1\nworried that the control plane and the agent would each compute this\nname and silently disagree — `ovn-controller` binds nothing when an\n`iface-id` matches no logical port, with no error anywhere, and the VM\nsimply has no network. The design's answer was a shared pure function;\ncarrying the string is stronger, because there is then only one\ncomputation rather than two that must agree.",
            "type": [
              "string",
              "null"
            ]
          },
          "vlan": {
            "description": "802.1Q VLAN tag, if any.",
            "format": "int32",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "name",
          "bridge",
          "mac"
        ],
        "type": "object"
      },
      "HostPowerState": {
        "description": "Power state as the agent observes it.",
        "enum": [
          "stopped",
          "running",
          "paused",
          "error"
        ],
        "type": "string"
      },
      "HostPruneReport": {
        "description": "What one prune produced.",
        "properties": {
          "pruned": {
            "description": "The pruned runs' ids, oldest first — the catalog order they were\nremoved in.",
            "items": {
              "format": "uuid",
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "pruned"
        ],
        "type": "object"
      },
      "HostPruneRequest": {
        "description": "Body of `POST /api/v1/vms/{id}/backups/actions/prune`: keep the newest\n`keep` backups of this VM, unbind the rest from the volume.",
        "properties": {
          "keep": {
            "description": "How many of the newest backups to keep; the floor is 1, because a\nprune that keeps nothing is what \"delete every backup\" means and\nno caller of this endpoint means that.",
            "format": "int32",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "keep"
        ],
        "type": "object"
      },
      "HostReplicateRequest": {
        "description": "Body of `POST /api/v1/vms/{id}/actions/replicate` (M4, contract §4.9,\narchitecture decision A16): control plane → source agent, normal\njoin-token auth. Sends every disk named in `disks` to `target_address`'s\nmatching VM, one `zfs send` per disk.",
        "properties": {
          "disks": {
            "description": "Disks to replicate, by name — exactly `spec.disks`' names.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "target_address": {
            "description": "Base URL of the target agent, e.g. `http://10.0.250.142:8090`.",
            "type": "string"
          },
          "target_datastore": {
            "$ref": "#/components/schemas/HostDatastoreRef",
            "description": "Datastore on the target host that receives the replica's disks."
          },
          "target_vm_id": {
            "description": "The target `Vm`'s id, exactly as the target's `/replication/expect` was told.",
            "format": "uuid",
            "type": "string"
          },
          "task_id": {
            "description": "This replication cycle's task id. Names the ZFS snapshot this cycle\ncreates (`replicate-<task_id>`), so a later cycle can find its own\nprior marker by listing snapshots rather than the control plane\ntracking snapshot names itself — the same reasoning as\n[`HostSnapshotRequest::snapshot_id`], reused rather than inventing a\nsecond id scheme for the same purpose.",
            "format": "uuid",
            "type": "string"
          },
          "token": {
            "description": "Per-transfer token this agent presents to the target's\n`/replication/receive` — never the join token (see\n[`HostReplicationExpectRequest`]'s doc).",
            "type": "string"
          }
        },
        "required": [
          "token",
          "task_id",
          "target_address",
          "target_vm_id",
          "target_datastore",
          "disks"
        ],
        "type": "object"
      },
      "HostReplicationExpectRequest": {
        "description": "Body of `POST /api/v1/replication/expect` (M4, contract §4.9, architecture\ndecision A16): control plane → target agent, normal join-token auth.\n\nRegisters a one-time expectation this agent checks when the matching\n`POST /api/v1/replication/receive/{vm_id}/{disk_name}` call arrives,\nauthenticated by `token` alone — never the join token, since that call\ncomes from a different host, which does not know this one's.",
        "properties": {
          "datastore": {
            "$ref": "#/components/schemas/HostDatastoreRef",
            "description": "Datastore that receives the replica's disks; must be `zfs`-kind."
          },
          "disks": {
            "description": "Disks this transfer will send, by name.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "token": {
            "description": "Per-transfer token; the same value the source agent presents as\n`Authorization: Bearer <token>` on `/replication/receive`.",
            "type": "string"
          },
          "vm_id": {
            "description": "The target `Vm`'s id, exactly the control plane's own `Vm.id`.",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "token",
          "vm_id",
          "datastore",
          "disks"
        ],
        "type": "object"
      },
      "HostRestoreDisk": {
        "description": "One disk's restore outcome.",
        "properties": {
          "bytes": {
            "description": "Bytes written by the streamed restore.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "name": {
            "description": "Disk name, as [`HostDiskSpec::name`] spells it.",
            "type": "string"
          },
          "sha256": {
            "description": "The whole-file SHA-256 the sidecar verified the reassembled bytes\nagainst — the manifest's own digest, checked chunk-by-chunk on the\nway in.",
            "type": "string"
          },
          "verified": {
            "description": "Whether that digest also matches the backup record's — a second,\nindependent confirmation that what came back is what went in.",
            "type": "boolean"
          }
        },
        "required": [
          "name",
          "bytes",
          "sha256",
          "verified"
        ],
        "type": "object"
      },
      "HostRestoreReport": {
        "description": "What one restore (or scratch verification — the same job, different\ndestination) produced.",
        "properties": {
          "backup_id": {
            "format": "uuid",
            "type": "string"
          },
          "disks": {
            "description": "Every disk, in record order.",
            "items": {
              "$ref": "#/components/schemas/HostRestoreDisk"
            },
            "type": "array"
          },
          "vm_id": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "backup_id",
          "vm_id",
          "disks"
        ],
        "type": "object"
      },
      "HostScrubFinding": {
        "description": "One scrub finding.",
        "properties": {
          "path": {
            "description": "The volume path whose integrity failed.",
            "type": "string"
          },
          "problem": {
            "description": "What failed, in the sidecar's own wording.",
            "type": "string"
          }
        },
        "required": [
          "path",
          "problem"
        ],
        "type": "object"
      },
      "HostScrubReport": {
        "description": "What one volume scrub found: every path the journal has a hash for,\nre-read and re-verified, and which of them (if any) no longer match.",
        "properties": {
          "findings": {
            "description": "Paths whose bytes no longer match their journal — empty is the\nhealthy answer, and any entry names a specific corruption.",
            "items": {
              "$ref": "#/components/schemas/HostScrubFinding"
            },
            "type": "array"
          },
          "targets": {
            "description": "Paths checked.",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "targets",
          "findings"
        ],
        "type": "object"
      },
      "HostSendMigrationRequest": {
        "description": "Body of `PUT /api/v1/vms/{id}/actions/send-migration` (M3), join-token\nauth. Blocks for the real cloud-hypervisor migration duration; on success\nthe local VMM this agent was driving has exited (cloud-hypervisor's own\ndocumented behavior for a successful live migration source).",
        "properties": {
          "destination": {
            "description": "Where to send the migration stream, e.g. `\"tcp:10.0.250.142:9001\"` —\nalways the target's real address and [`MIGRATION_PORT`], never a\nUnix socket (the two hosts are always different machines here).",
            "type": "string"
          }
        },
        "required": [
          "destination"
        ],
        "type": "object"
      },
      "HostSnapshotRequest": {
        "description": "Body of `POST /api/v1/vms/{id}/snapshots` (M2, contract §4.9): capture the\nVM's current disk state under `snapshot_id`.\n\nThe control plane generates `snapshot_id`, not the agent — it is already\nthe primary key of the control plane's own `snapshots` row, so both sides\nname the same point-in-time state by the same id (mirrors how `HostVmSpec`\ncarries the control plane's `Vm.id` rather than letting the agent invent\none). The agent has no other record of a VM's snapshots; the control\nplane's database is the only list.",
        "properties": {
          "live": {
            "description": "Capture memory/device state too, without stopping the VM first (M3,\narchitecture §5.16/A25). Create only — the matching revert route\ntakes no body at all; whether a given snapshot is live is inferred\nthere from whether its memory-snapshot directory exists on disk, the\nsame \"trust real state, not a caller-supplied flag\" idiom\n`finalize-migration` already established, since revert has no reason\nto ask the caller to repeat what the snapshot itself already recorded.",
            "type": "boolean"
          },
          "snapshot_id": {
            "description": "Identifies this snapshot for a later revert or delete.",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "snapshot_id"
        ],
        "type": "object"
      },
      "HostSpareTireDisk": {
        "description": "One disk of a spare tire.",
        "properties": {
          "manifest_id": {
            "description": "The disk image's manifest id (`m-…`).",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "size_bytes": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "name",
          "manifest_id",
          "size_bytes"
        ],
        "type": "object"
      },
      "HostSpareTireRecord": {
        "description": "One whole-VM spare tire: the memory image and every disk, stored in\nthe volume as content-addressed manifests, restorable on any host\nthat can reach the object store (IDEAS.md #23).",
        "properties": {
          "created_at_unix": {
            "description": "Unix seconds, UTC.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "disks": {
            "description": "Every captured disk, in spec order.",
            "items": {
              "$ref": "#/components/schemas/HostSpareTireDisk"
            },
            "type": "array"
          },
          "memory_bytes": {
            "description": "The memory image's byte count.",
            "format": "int64",
            "type": "integer"
          },
          "memory_manifest": {
            "description": "The memory image's manifest id (`m-…`).",
            "type": "string"
          },
          "spec": {
            "$ref": "#/components/schemas/HostVmSpec",
            "description": "The VM's spec at capture time — the restore's template, with\ndisks rewritten to volume-backed."
          },
          "vm_id": {
            "description": "The VM this record captures; the restore recreates the same id.",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "vm_id",
          "created_at_unix",
          "memory_manifest",
          "memory_bytes",
          "disks",
          "spec"
        ],
        "type": "object"
      },
      "HostStatsResponse": {
        "description": "Response of `GET /api/v1/stats` (M2): the host's own usage plus every VM\nthis agent currently has a running process for, all sampled together in\none ~200ms window so they describe the same instant.\n\nA CPU percentage needs two samples over a real time delta — this\nendpoint takes that delta itself (a brief internal sleep) rather than\nrequiring the caller to poll twice, so one request always answers a\ncomplete, real reading. A VM with no live process (stopped, paused, or\njust not found) is omitted from `vms` rather than reported as `0%` —\nthat would misrepresent \"no data\" as \"idle\".",
        "properties": {
          "host": {
            "$ref": "#/components/schemas/ResourceUsage",
            "description": "The host's own CPU and memory usage."
          },
          "vms": {
            "description": "Usage of every VM with a live process right now.",
            "items": {
              "$ref": "#/components/schemas/VmResourceUsage"
            },
            "type": "array"
          }
        },
        "required": [
          "host",
          "vms"
        ],
        "type": "object"
      },
      "HostUploadImageQuery": {
        "description": "Query of `PUT /api/v1/images/upload` (M2, contract §4.8): the body is the\nraw upload itself (`Content-Type: application/octet-stream`), so the\ndestination travels as query parameters instead of a JSON body.",
        "properties": {
          "datastore_id": {
            "description": "The datastore's own id (see [`HostDatastoreRef::datastore_id`]).",
            "format": "uuid",
            "type": "string"
          },
          "dest_path": {
            "description": "Destination path relative to the datastore's root.",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/HostDatastoreKind",
            "description": "Storage backend."
          },
          "path": {
            "description": "ZFS dataset name, absolute directory path, or (`nfs`) export source.",
            "type": "string"
          }
        },
        "required": [
          "kind",
          "path",
          "datastore_id",
          "dest_path"
        ],
        "type": "object"
      },
      "HostVmList": {
        "description": "Response of `GET /api/v1/vms`.",
        "properties": {
          "count": {
            "description": "Number of VMs.",
            "minimum": 0,
            "type": "integer"
          },
          "vms": {
            "description": "Managed VMs.",
            "items": {
              "$ref": "#/components/schemas/HostVmStatus"
            },
            "type": "array"
          }
        },
        "required": [
          "vms",
          "count"
        ],
        "type": "object"
      },
      "HostVmResponse": {
        "description": "Response wrapping one VM status.",
        "properties": {
          "vm": {
            "$ref": "#/components/schemas/HostVmStatus",
            "description": "The VM."
          }
        },
        "required": [
          "vm"
        ],
        "type": "object"
      },
      "HostVmSpec": {
        "description": "Body of `POST /api/v1/vms`: a complete, host-local VM definition.",
        "properties": {
          "boot": {
            "$ref": "#/components/schemas/HostBootSpec",
            "description": "Boot method."
          },
          "cpus": {
            "description": "Virtual CPUs.",
            "format": "int32",
            "minimum": 0,
            "type": "integer"
          },
          "disks": {
            "description": "Disks, boot disk first.",
            "items": {
              "$ref": "#/components/schemas/HostDiskSpec"
            },
            "type": "array"
          },
          "id": {
            "description": "Control-plane identifier; the agent uses it for sockets, disks, and state.",
            "format": "uuid",
            "type": "string"
          },
          "memory_bytes": {
            "description": "Guest memory.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          },
          "name": {
            "description": "Name, used for tap-device prefixes and logs.",
            "type": "string"
          },
          "nics": {
            "description": "Network interfaces.",
            "items": {
              "$ref": "#/components/schemas/HostNicSpec"
            },
            "type": "array"
          },
          "start": {
            "description": "Boot immediately after creation.",
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "name",
          "cpus",
          "memory_bytes",
          "boot",
          "disks",
          "nics",
          "start"
        ],
        "type": "object"
      },
      "HostVmStatus": {
        "description": "Observed state of one VM on the host.",
        "properties": {
          "id": {
            "description": "Control-plane identifier.",
            "format": "uuid",
            "type": "string"
          },
          "message": {
            "description": "Latest detail, typically the last error.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "Name.",
            "type": "string"
          },
          "pid": {
            "description": "PID of the VMM process while running.",
            "format": "int32",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "power": {
            "$ref": "#/components/schemas/HostPowerState",
            "description": "Power state."
          }
        },
        "required": [
          "id",
          "name",
          "power"
        ],
        "type": "object"
      },
      "InstallCertificateRequest": {
        "description": "Body of `POST /api/v1/enroll/cert` (`MTLS.md` slice 3).\n\nBoth fields are PEM. The agent writes them to disk as its server\ncertificate and trusted CA respectively; neither is trusted or verified\nagain by the agent itself, because the agent has no way to know a CA is\nlegitimate — verifying that the *pair* is legitimate together is the\ncontrol plane's job, done once, before this call is ever made.",
        "properties": {
          "ca_certificate_pem": {
            "description": "The CA certificate the agent's own mTLS listener will require future\nclient certificates to chain to.",
            "type": "string"
          },
          "certificate_pem": {
            "description": "This host's signed certificate, chaining to `ca_certificate_pem`.",
            "type": "string"
          }
        },
        "required": [
          "certificate_pem",
          "ca_certificate_pem"
        ],
        "type": "object"
      },
      "InstallCertificateResponse": {
        "description": "Response of `POST /api/v1/enroll/cert`.",
        "properties": {
          "mtls_active": {
            "description": "Whether the agent's mTLS listener is live *right now*, in this\nprocess, without a restart.\n\nDistinct from \"the files were written\": a listener that fails to bind\n(a port already in use, most plausibly) still leaves valid material on\ndisk that a future restart would pick up, and the caller needs to know\nwhich of those two states it actually got, rather than assume success\nbecause the HTTP call itself returned `200`.",
            "type": "boolean"
          }
        },
        "required": [
          "mtls_active"
        ],
        "type": "object"
      },
      "ResourceUsage": {
        "description": "CPU and memory usage of one thing at the moment it was sampled (M2,\ncontract §4.9/§4.5's metrics rows) — the host itself, or one VM's VMM\nprocess. Never simulated: both sides are read from `/proc` (`/proc/stat`\nand `/proc/meminfo` for the host; `/proc/<pid>/stat` and\n`/proc/<pid>/status` for a VM's own process — the same technique\nregardless of which hypervisor owns that pid, since both are ordinary\nLinux processes with a real pid this agent already tracks).",
        "properties": {
          "cpu_percent": {
            "description": "Percentage of one CPU-second consumed per wall-clock second since the\nprevious sample, `0.0..=(100.0 * cpus)` for the host or `0.0..=100.0`\nper vCPU for a VM — never negative, never fabricated when a rate\ncannot be computed (see [`HostStatsResponse`]'s doc for when that\nhappens).",
            "format": "double",
            "type": "number"
          },
          "memory_bytes": {
            "description": "Resident memory in use: total minus available for the host, `VmRSS`\nfor a VM's process.",
            "format": "int64",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "cpu_percent",
          "memory_bytes"
        ],
        "type": "object"
      },
      "VmResourceUsage": {
        "description": "One VM's [`ResourceUsage`], identified by its control-plane id.",
        "properties": {
          "id": {
            "description": "Control-plane identifier (this agent's own `HostVmSpec.id`).",
            "format": "uuid",
            "type": "string"
          },
          "usage": {
            "$ref": "#/components/schemas/ResourceUsage",
            "description": "This VM's usage."
          }
        },
        "required": [
          "id",
          "usage"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearer_token": {
        "scheme": "bearer",
        "type": "http"
      },
      "replication_token": {
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "Internal API the control plane uses to drive one KVM host.",
    "license": {
      "name": ""
    },
    "title": "nextVIRT Host Agent API",
    "version": "0.1.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/backups/gc": {
      "post": {
        "operationId": "volume_gc",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostGcRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostGcReport"
                }
              }
            },
            "description": "the pass's counts — check `ran` for the lease outcome"
          },
          "502": {
            "description": "the nfd sidecar failed"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "backups"
        ]
      }
    },
    "/api/v1/backups/scrub": {
      "post": {
        "operationId": "volume_scrub",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostScrubReport"
                }
              }
            },
            "description": "every journaled path re-verified; empty findings is healthy"
          },
          "422": {
            "description": "backup_location_unset"
          },
          "502": {
            "description": "the nfd sidecar failed"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "backups"
        ]
      }
    },
    "/api/v1/datastores/stats": {
      "get": {
        "operationId": "stats",
        "parameters": [
          {
            "description": "Storage backend.",
            "in": "query",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/HostDatastoreKind"
            }
          },
          {
            "description": "ZFS dataset name, absolute directory path, or (`nfs`) export source.",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The datastore's own id (see [`HostDatastoreRef::datastore_id`]).",
            "in": "query",
            "name": "datastore_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DatastoreStats"
                }
              }
            },
            "description": ""
          },
          "404": {
            "description": "not_found"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "datastores"
        ]
      }
    },
    "/api/v1/enroll/cert": {
      "post": {
        "operationId": "install_certificate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InstallCertificateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstallCertificateResponse"
                }
              }
            },
            "description": ""
          },
          "422": {
            "description": "no certificate request is pending for this host"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "enroll"
        ]
      }
    },
    "/api/v1/enroll/csr": {
      "get": {
        "operationId": "enroll_csr",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnrollCsrResponse"
                }
              }
            },
            "description": ""
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "enroll"
        ]
      }
    },
    "/api/v1/fencing/arm": {
      "post": {
        "description": "Idempotent. Refuses a driver that supports neither Magic Close nor\n`nowayout`, because a crashing agent would silently disarm such a fence —\nand HA restart would then trust a fence that is not there.",
        "operationId": "arm",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FencingInfo"
                }
              }
            },
            "description": ""
          },
          "422": {
            "description": "no watchdog, or one that cannot be trusted"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Arms the watchdog, so this host resets if the control plane stops polling it.",
        "tags": [
          "fencing"
        ]
      }
    },
    "/api/v1/fencing/disarm": {
      "post": {
        "description": "Idempotent. If the magic character cannot be written the fence is left\narmed rather than dropped — dropping it would reset the host, which is the\nopposite of what a disarm request asked for.",
        "operationId": "disarm",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FencingInfo"
                }
              }
            },
            "description": ""
          },
          "422": {
            "description": "this host has no watchdog"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Releases the watchdog cleanly, via Magic Close.",
        "tags": [
          "fencing"
        ]
      }
    },
    "/api/v1/images/import": {
      "post": {
        "operationId": "import",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostImportImageRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostImportImageResponse"
                }
              }
            },
            "description": ""
          },
          "422": {
            "description": "checksum_mismatch"
          },
          "502": {
            "description": "the url could not be fetched, or an nfs export could not be mounted"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "images"
        ]
      }
    },
    "/api/v1/images/upload": {
      "put": {
        "operationId": "upload",
        "parameters": [
          {
            "description": "Storage backend.",
            "in": "query",
            "name": "kind",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/HostDatastoreKind"
            }
          },
          {
            "description": "ZFS dataset name, absolute directory path, or (`nfs`) export source.",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The datastore's own id (see [`HostDatastoreRef::datastore_id`]).",
            "in": "query",
            "name": "datastore_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "description": "Destination path relative to the datastore's root.",
            "in": "query",
            "name": "dest_path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/octet-stream": {}
          },
          "description": "the raw file"
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostImportImageResponse"
                }
              }
            },
            "description": ""
          },
          "502": {
            "description": "the stream was interrupted, or an nfs export could not be mounted"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "images"
        ]
      }
    },
    "/api/v1/info": {
      "get": {
        "operationId": "info",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostInfo"
                }
              }
            },
            "description": ""
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "This host's capacity and the agent's own version, polled every 5s (contract §4.5).",
        "tags": [
          "system"
        ]
      }
    },
    "/api/v1/interfaces/confirm": {
      "post": {
        "operationId": "confirm",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostInterfaceConfirmRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "{ confirmed }"
          },
          "409": {
            "description": "no_pending_change"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Keeps a change the caller has reached the host to verify.",
        "tags": [
          "interfaces"
        ]
      }
    },
    "/api/v1/interfaces/{name}": {
      "put": {
        "operationId": "update",
        "parameters": [
          {
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Seconds; clamped to 30–600, defaulting to 120.",
            "in": "query",
            "name": "confirm_within_secs",
            "required": false,
            "schema": {
              "format": "int64",
              "minimum": 0,
              "type": [
                "integer",
                "null"
              ]
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostInterfaceUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostInterfaceChange"
                }
              }
            },
            "description": ""
          },
          "422": {
            "description": "invalid_interface, unknown_interface"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Applies a provisional change to one interface.",
        "tags": [
          "interfaces"
        ]
      }
    },
    "/api/v1/replication/expect": {
      "post": {
        "operationId": "expect",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostReplicationExpectRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "{ accepted: true }"
          },
          "422": {
            "description": "datastore_not_zfs"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "replication"
        ]
      }
    },
    "/api/v1/replication/receive/{vm_id}/{disk_name}": {
      "post": {
        "operationId": "receive",
        "parameters": [
          {
            "in": "path",
            "name": "vm_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "disk_name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/octet-stream": {}
          },
          "description": "a zfs send stream"
        },
        "responses": {
          "200": {
            "description": "{ received: true }"
          },
          "401": {
            "description": "the token is missing, unknown, already used, or expired"
          },
          "502": {
            "description": "zfs receive failed, or the stream was interrupted"
          }
        },
        "security": [
          {
            "replication_token": []
          }
        ],
        "tags": [
          "replication"
        ]
      }
    },
    "/api/v1/stats": {
      "get": {
        "operationId": "get_stats",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostStatsResponse"
                }
              }
            },
            "description": ""
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Live host and per-VM CPU/memory usage (contract §4.5, M2).",
        "tags": [
          "stats"
        ]
      }
    },
    "/api/v1/vms": {
      "get": {
        "operationId": "list",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostVmList"
                }
              }
            },
            "description": ""
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "vms"
        ]
      },
      "post": {
        "operationId": "create",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostVmSpec"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostVmResponse"
                }
              }
            },
            "description": ""
          },
          "409": {
            "description": "the id already exists"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}": {
      "delete": {
        "operationId": "delete_one",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ deleted }"
          },
          "404": {
            "description": "not_found"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "vms"
        ]
      },
      "get": {
        "operationId": "get_one",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostVmResponse"
                }
              }
            },
            "description": ""
          },
          "404": {
            "description": "not_found"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/attach-iso": {
      "post": {
        "operationId": "attach_iso",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostAttachIsoRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "{ attached: true }"
          },
          "404": {
            "description": "not_found"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Attaches an image read-only to the VM's optical drive (M2, contract\n§4.9). The control plane has already resolved `image_id` to an absolute\nhost path — this agent has no inventory model to look one up in.",
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/backup": {
      "post": {
        "operationId": "backup",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostBackupRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostBackupRecord"
                }
              }
            },
            "description": "the run's record"
          },
          "404": {
            "description": "not_found"
          },
          "422": {
            "description": "backup_location_unset"
          },
          "502": {
            "description": "the pause, the nfd sidecar, or the catalog write failed"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "backups"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/detach": {
      "post": {
        "operationId": "detach",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "local bookkeeping removed; datastore/disk files untouched"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "migration"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/detach-iso": {
      "post": {
        "operationId": "detach_iso",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ detached: true }"
          },
          "404": {
            "description": "not_found"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Detaches whatever is in the VM's optical drive, if anything (M2, contract\n§4.9). Idempotent.",
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/finalize-migration": {
      "post": {
        "operationId": "finalize_migration",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostVmSpec"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "local bookkeeping written"
          },
          "409": {
            "description": "name_taken — a VM with this id already exists on this host"
          },
          "502": {
            "description": "tap creation failed (cold path only)"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "migration"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/finalize-replica": {
      "post": {
        "description": "Network taps, by contrast, are created fresh here (unlike `respawn`,\nwhich assumes they already exist): this agent has never defined this VM\nbefore, so nothing has created them yet, exactly like an ordinary\n[`create`]. `spec.start` is expected `false` — the caller\n(`nextvirt-core::vms::promote_replica`) leaves the finalized VM `stopped`\nfor the operator to start explicitly, not booted mid-promotion.",
        "operationId": "finalize_replica",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostVmSpec"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostVmResponse"
                }
              }
            },
            "description": ""
          },
          "409": {
            "description": "the id already exists"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Defines a VM whose disks already exist on this host, without creating\nthem (M4, decision A16): the last step of async DR replication's\n`promote-replica`, once every disk has arrived via `zfs receive`\n(`api/replication.rs`). Unlike [`create`], never calls\n[`crate::backends::Storage::create_disk`] — doing so would try to `zfs\ncreate` a dataset `zfs receive` already populated and fail with \"dataset\nalready exists\" — instead resolving each disk's path the same\nno-I/O way [`respawn`] does for a VM whose disks are already on disk.",
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/receive-migration": {
      "post": {
        "operationId": "receive_migration",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostVmSpec"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "spawned and listening; the transfer itself is not yet complete"
          },
          "409": {
            "description": "name_taken — a VM with this id already exists on this host"
          },
          "502": {
            "description": "the tap could not be created, or the VMM could not be spawned"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "migration"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/replicate": {
      "post": {
        "operationId": "replicate",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostReplicateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "{ replicated: true }"
          },
          "404": {
            "description": "not_found"
          },
          "502": {
            "description": "a zfs command failed, or the target refused the transfer"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "replication"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/send-migration": {
      "post": {
        "operationId": "send_migration",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostSendMigrationRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "{ migrated: true } — the local VMM has exited"
          },
          "404": {
            "description": "not_found"
          },
          "502": {
            "description": "the VMM reported a migration failure"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "migration"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/spare-tire": {
      "post": {
        "operationId": "spare_tire_capture",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostSpareTireRecord"
                }
              }
            },
            "description": "memory + every disk captured into the volume; the record"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "vm_not_running | spare_tire_needs_qemu"
          },
          "422": {
            "description": "backup_location_unset | volume_disk_capture_unsupported"
          },
          "502": {
            "description": "a QMP, sidecar, or record failure"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "backups"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/spare-tire-restore": {
      "post": {
        "operationId": "spare_tire_restore",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostSpareTireRecord"
                }
              }
            },
            "description": "the VM recreated from the volume on THIS host and resumed from its memory image"
          },
          "409": {
            "description": "name_taken"
          },
          "422": {
            "description": "backup_location_unset"
          },
          "502": {
            "description": "the record, image, or replay failed"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "backups"
        ]
      }
    },
    "/api/v1/vms/{id}/actions/{action}": {
      "post": {
        "operationId": "action",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "description": "start | stop | reset | pause | resume",
            "in": "path",
            "name": "action",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Ask the guest first via ACPI, waiting up to `timeout_seconds` before forcing it off.",
            "in": "query",
            "name": "graceful",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "Seconds to wait for a graceful stop before forcing it off.",
            "in": "query",
            "name": "timeout_seconds",
            "required": false,
            "schema": {
              "format": "int32",
              "minimum": 0,
              "type": [
                "integer",
                "null"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostVmResponse"
                }
              }
            },
            "description": ""
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "illegal_transition"
          },
          "501": {
            "description": "not_implemented: graceful stop on a kernel-boot vm"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}/backups": {
      "get": {
        "operationId": "list",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostBackupList"
                }
              }
            },
            "description": "the agent-local catalog, oldest first"
          },
          "502": {
            "description": "the catalog cannot be read or a record cannot be parsed"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "backups"
        ]
      }
    },
    "/api/v1/vms/{id}/backups/actions/prune": {
      "post": {
        "operationId": "prune",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostPruneRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostPruneReport"
                }
              }
            },
            "description": "the pruned runs' ids, oldest first"
          },
          "404": {
            "description": "not_found"
          },
          "422": {
            "description": "keep_must_be_at_least_one"
          },
          "502": {
            "description": "the nfd sidecar failed"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "backups"
        ]
      }
    },
    "/api/v1/vms/{id}/backups/{backup_id}/actions/restore": {
      "post": {
        "operationId": "restore",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "backup_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostRestoreReport"
                }
              }
            },
            "description": "every disk restored and verified"
          },
          "404": {
            "description": "not_found"
          },
          "409": {
            "description": "vm_not_stopped"
          },
          "422": {
            "description": "disk_no_longer_exists | disk_size_mismatch"
          },
          "502": {
            "description": "the nfd sidecar failed, or a digest did not match"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "backups"
        ]
      }
    },
    "/api/v1/vms/{id}/backups/{backup_id}/actions/verify": {
      "post": {
        "operationId": "verify",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "backup_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostRestoreReport"
                }
              }
            },
            "description": "every disk reassembled to scratch and verified; scratch removed"
          },
          "404": {
            "description": "not_found"
          },
          "502": {
            "description": "the nfd sidecar failed, or a digest did not match"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "tags": [
          "backups"
        ]
      }
    },
    "/api/v1/vms/{id}/console": {
      "get": {
        "description": "Binary frames carry terminal bytes both directions; text frames are the\n`ping`/`pong`/`role` JSON control messages. Closes with `4404` if `id` is\nunknown, `4409` if it is not running, `1011` on a host-side error.",
        "operationId": "console",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "WebSocket upgrade: serial console bytes"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Serial console over a WebSocket (contract §4.9, architecture §6.5).",
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}/snapshots": {
      "post": {
        "operationId": "snapshot_create",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostSnapshotRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "{ snapshot_id }"
          },
          "404": {
            "description": "not_found"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Captures the VM's current disk state under `req.snapshot_id` (M2,\ncontract §4.9). The control plane generates the id (see\n[`HostSnapshotRequest`]'s doc) and is the only place a snapshot's\nmetadata (name, timestamp) lives — this agent only ever executes create,\nrevert, and delete against the id it is given.",
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}/snapshots/{snapshot_id}": {
      "delete": {
        "operationId": "snapshot_delete",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "snapshot_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ deleted }"
          },
          "404": {
            "description": "not_found"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Removes `snapshot_id` from every disk (M2, contract §4.9). Idempotent.",
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}/snapshots/{snapshot_id}/revert": {
      "post": {
        "operationId": "snapshot_revert",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "snapshot_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ snapshot_id }"
          },
          "404": {
            "description": "not_found"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "Restores every disk to its content at `snapshot_id` (M2, contract §4.9).\nThe caller (`Core::revert_snapshot`) is responsible for the VM being\nstopped first; this does not check. Whether `snapshot_id` is a *live*\nsnapshot is inferred from real, on-disk state — `snapshot_dir(id,\nsnapshot_id)` existing — rather than trusted from a caller-supplied flag,\nthe same idiom `finalize-migration` already established (revert takes no\nbody at all; the snapshot's own recorded `live` field lives only in the\ncontrol plane's database, which this agent has no access to).",
        "tags": [
          "vms"
        ]
      }
    },
    "/api/v1/vms/{id}/vnc": {
      "get": {
        "operationId": "vnc",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "WebSocket upgrade: raw RFB protocol bytes"
          }
        },
        "security": [
          {
            "bearer_token": []
          }
        ],
        "summary": "VNC console over a WebSocket, for firmware-boot VMs only (contract §4.9,\narchitecture §6.5). Binary frames carry raw RFB protocol bytes both\ndirections — no text control channel, unlike the serial console, since\nRFB has none. Closes with `4404` if `id` is unknown, `4409` if it is not\nrunning, `1011` on a host-side error.",
        "tags": [
          "vms"
        ]
      }
    },
    "/health": {
      "get": {
        "operationId": "health",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HostHealth"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Unauthenticated liveness probe.",
        "tags": [
          "system"
        ]
      }
    },
    "/metrics": {
      "get": {
        "description": "Mounted outside the bearer-authenticated route group (`lib.rs`), matching\n`/health`'s existing unauthenticated precedent — `ARCHITECTURE.md` §11\ngroups `/metrics` with `/health`, not with `/api/v1/...`, since Prometheus\nscraping trusts the network, not an app-level token.",
        "operationId": "prometheus_metrics",
        "responses": {
          "200": {
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Prometheus exposition text"
          }
        },
        "summary": "The same sample as [`stats`], rendered as Prometheus exposition text.",
        "tags": [
          "stats"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "This host",
      "url": "/"
    }
  ],
  "tags": [
    {
      "description": "Health and host info",
      "name": "system"
    },
    {
      "description": "Virtual machine lifecycle",
      "name": "vms"
    },
    {
      "description": "Datastore usage",
      "name": "datastores"
    },
    {
      "description": "Async DR replication (M4)",
      "name": "replication"
    },
    {
      "description": "VM backups into a chunk-store volume through the nfd sidecar",
      "name": "backups"
    },
    {
      "description": "Watchdog fencing, so silence proves this host reset (M3)",
      "name": "fencing"
    },
    {
      "description": "Host network interfaces, changed provisionally with rollback",
      "name": "interfaces"
    }
  ]
}
