{
  "components": {
    "schemas": {
      "AcknowledgeReq": {
        "description": "An operator's acknowledgement that the other region's copy of the workload is the one discarded.",
        "properties": {
          "confirm_other_region_discarded": {
            "description": "The acknowledgement that the other region's copy is discarded and this one keeps running. Must be true, or the request is refused with `acknowledge_unconfirmed`.",
            "type": "boolean"
          },
          "reason": {
            "description": "Why, recorded with the group and in the audit log.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "AcknowledgeResponse": {
        "description": "The group after an acknowledgement cleared its split brain.",
        "properties": {
          "custody_epoch": {
            "description": "The custody epoch this region now holds, above the other region's.",
            "format": "int64",
            "type": "integer"
          },
          "group": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupView"
              }
            ],
            "description": "The group, `protected` at its new custody epoch."
          },
          "record_run_id": {
            "description": "The id the acknowledgement was recorded under where the other region reads it.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "Anchors": {
        "description": "Where the periods of a retention ladder start.",
        "properties": {
          "month": {
            "description": "`first` keeps the first backup of each month; `last` keeps the first backup taken on or after the month's last day.",
            "enum": [
              "first",
              "last"
            ],
            "type": "string"
          },
          "week": {
            "description": "The day a week starts, 0 for Sunday to 6 for Saturday.",
            "type": "integer"
          },
          "year": {
            "description": "The month a year starts, 1 for January to 12 for December.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "AnchorsReq": {
        "description": "Where the periods of a retention ladder start.",
        "properties": {
          "month": {
            "description": "`first` starts each monthly period on the first day of a month, so the backup kept is the first one taken in each month. `last` starts each period on a month's last day, so the backup kept is the one taken on the last day of each month. Defaults to `first`.",
            "enum": [
              "first",
              "last"
            ],
            "type": "string"
          },
          "week": {
            "description": "The day weeks start on, as a weekday name such as `sunday`, or 0 to 6 with 0 for Sunday. Defaults to `sunday`.",
            "type": "string"
          },
          "year": {
            "description": "The month years start in, as a month name such as `january`, or 1 to 12. Defaults to `january`.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ApplyResult": {
        "description": "What re-dating a policy's existing backups to its new retention did. Manual backups are left alone.",
        "properties": {
          "locked": {
            "description": "How many kept their date because the new one falls before the end of a lock still in force.",
            "type": "integer"
          },
          "rearmed": {
            "description": "How many backups were given a new expiry.",
            "type": "integer"
          },
          "relocking": {
            "description": "How many now expire later in a locked repository, whose locks are being extended to the new date. Absent when none.",
            "type": "integer"
          },
          "shortened": {
            "description": "Of those, how many now expire earlier than before.",
            "type": "integer"
          },
          "unshortenable": {
            "description": "The ids of up to 100 of the locked backups. Absent when none.",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ArmReq": {
        "description": "How long the delegation lasts.",
        "properties": {
          "expires_in": {
            "description": "How long the delegation lasts, as a duration from `1h` to `8784h`. Defaults to `2160h`, ninety days.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "AssignmentReq": {
        "description": "Domains and projects, by their Keystone ids. The ids are not checked against Keystone.",
        "properties": {
          "domains": {
            "description": "Domain ids. Every project of each domain is offered the policy.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "projects": {
            "description": "Project ids. Each project is offered the policy, whatever its domain.",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "AttachmentListView": {
        "description": "An attachment of a backup to a server, and where it is in its life.",
        "properties": {
          "created_at": {
            "description": "When the attachment was requested.",
            "format": "date-time",
            "type": "string"
          },
          "device": {
            "description": "The device name requested for it in the guest. Absent when none was requested.",
            "type": "string"
          },
          "expires_at": {
            "description": "When the attachment is detached and cleaned up automatically: `expires_in` after it was attached, 24 hours when the request named none. Absent until it is attached.",
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "The attachment's id.",
            "type": "string"
          },
          "job_id": {
            "description": "The job attaching it, or once a detach has been asked for, the job detaching it.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup attached.",
            "type": "string"
          },
          "server_id": {
            "description": "The server it is attached to.",
            "type": "string"
          },
          "source_tier": {
            "description": "The copy it was attached from, `chunk`, the repository copy.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          },
          "state": {
            "description": "`pending`: queued. `running`: the backup is being restored into a temporary volume. `succeeded`: the volume is attached to the server and usable. `failed`: it could not be attached. `detaching`: it is being detached and the volume deleted. `detached`: it is gone.",
            "enum": [
              "pending",
              "running",
              "succeeded",
              "failed",
              "detaching",
              "detached"
            ],
            "type": "string"
          },
          "volume_id": {
            "description": "The temporary volume presented to the server. Absent until the volume exists.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "AttachmentListViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/AttachmentListView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "AttachmentView": {
        "description": "An attachment that has just been accepted. Poll GET /v1/attachments, or the job, until it is attached.",
        "properties": {
          "estimated_restore_seconds": {
            "description": "How long preparing the volume is expected to take, in seconds.",
            "format": "int64",
            "type": "integer"
          },
          "id": {
            "description": "The attachment's id. Use it to detach.",
            "type": "string"
          },
          "job_id": {
            "description": "The job attaching it.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup being attached.",
            "type": "string"
          },
          "source_tier": {
            "description": "The copy it is attached from, `chunk`, the repository copy.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          },
          "state": {
            "description": "Always `pending` in this answer: the attachment is queued. GET /v1/attachments then shows where it is. `running`: the backup is being restored into a temporary volume. `succeeded`: the volume is attached to the server and usable. `failed`: it could not be attached. `detaching`: it is being detached and the volume deleted, on request or at `expires_at`. `detached`: it is gone.",
            "enum": [
              "pending"
            ],
            "type": "string"
          },
          "target_instance_id": {
            "description": "The server it is being attached to.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "BackupTier": {
        "description": "The repository every backup of a policy is written to.",
        "properties": {
          "repository_id": {
            "description": "The repository's id.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "BackupTierReq": {
        "description": "The repository every backup is written to.",
        "properties": {
          "repository_id": {
            "description": "The id of a registered chunk repository. Its vault, if it has one, sets the terms for manual backups in plans under the policy.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "BindingDryRunReq": {
        "description": "What to resolve. All fields are optional, and the body may be left out.",
        "properties": {
          "confirm_unresolved": {
            "description": "Unresolved elements to accept as they are, each as `element:source` exactly as the resolution lists it, or as `element` to accept every entry of that element. Matched without regard to case.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "mapping_profile": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Profile"
              }
            ],
            "description": "A translation given inline instead of a stored profile."
          },
          "point_id": {
            "description": "The adopted backup to resolve. Defaults to the newest one the binding adopted.",
            "type": "string"
          },
          "profile": {
            "description": "The name of a stored recovery profile to translate with. Cannot be combined with `mapping_profile`. With neither, no profile is used, and elements are matched by name where they can be.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "BindingDryRunResult": {
        "description": "What recovering a bound resource here would build, what will differ from the source, and what cannot be mapped. Nothing was created.",
        "properties": {
          "binding_id": {
            "description": "The binding that was resolved.",
            "type": "string"
          },
          "blockers": {
            "description": "Conditions of this service that would make the recovery fail even with the mapping resolved, such as an account that cannot act on a user's behalf. No confirmation clears them. Absent when there are none.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "committable": {
            "description": "Whether a recovery could go ahead: true when every unresolved element is confirmed and nothing is listed under `blockers`. Can differ from `resolution.committable`, which ignores confirmations.",
            "type": "boolean"
          },
          "point_id": {
            "description": "The backup that was resolved.",
            "type": "string"
          },
          "refusal": {
            "description": "Which unresolved elements still need confirming. Absent when none do.",
            "type": "string"
          },
          "repository": {
            "description": "The name of the repository the backup was read from.",
            "type": "string"
          },
          "resolution": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Resolution"
              }
            ],
            "description": "How the backup maps onto this cloud, element by element. It lists what resolved, what did not and what resolves differently from the source, then the decisions a recovery would carry out and every object it would create, with totals. It is worked out before `confirm_unresolved` is applied."
          }
        },
        "type": "object"
      },
      "BubblePolicy": {
        "description": "An operator's policy for the isolated network a test runs in. A test builds that network in the policy's operator project, proves it isolated, boots copies of the group's members on it, checks their health from a jump host inside it, and tears everything down. The tenant's own project is never touched.",
        "properties": {
          "created_at": {
            "description": "When the policy was created.",
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "The policy's id.",
            "type": "string"
          },
          "isolation_canaries": {
            "description": "Addresses, each as `host:port`, that the isolated network must not be able to reach. Before any member boots, the jump host tries each one: a test that reaches one fails with `isolation_breached`, and one whose jump host does not report within ten minutes fails with `isolation_unproven`. Empty when none are set.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "jump_host": {
            "description": "Whether a test brings up a jump host on the isolated network to reach the copies. A test brings one up anyway when it has health checks to run or isolation canaries to try.",
            "type": "boolean"
          },
          "jump_host_flavor": {
            "description": "The flavor the jump host uses. Absent when not set, and the service's own probe flavor is used instead.",
            "type": "string"
          },
          "jump_host_image": {
            "description": "The image the jump host boots from. Absent when not set, and the service's own probe image is used instead.",
            "type": "string"
          },
          "max_duration": {
            "description": "The longest a test may run, in nanoseconds. A test still running then fails with `run_deadline_exceeded`, and everything it built is torn down; what it built also expires then even if the run is gone.",
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "description": "The policy's name, unique on this service.",
            "type": "string"
          },
          "network_cidrs": {
            "description": "The address ranges the isolated network's subnets are allocated from.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "permitted_checks": {
            "description": "The health check types a test may run. A test of a group with a member whose check is not listed is refused with `check_not_permitted`.",
            "items": {
              "enum": [
                "tcp",
                "http"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "project_id": {
            "description": "The operator project every test under this policy is built in: its network, its jump host and the copies of the members. Never a tenant's project.",
            "type": "string"
          },
          "strict": {
            "description": "When true, the copies may send traffic only to addresses inside the isolated network; when false, their outbound traffic is not filtered. Either way the network has no router, gateway or name servers, which a test proves before booting anything, failing with `isolation_unproven` otherwise. A strict policy with no jump host cannot run health checks or try isolation canaries.",
            "type": "boolean"
          },
          "updated_at": {
            "description": "When the policy last changed. The same as `created_at` until it first does.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "BubblePolicyPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/BubblePolicy"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "BubblePolicyReq": {
        "description": "A test policy's settings. On create, `name`, `project_id` and `network_cidrs` are required and the rest take their defaults. On change, only the fields sent change, and the result must still satisfy the rules of create.",
        "properties": {
          "isolation_canaries": {
            "description": "Addresses, each as `host:port` with a port from 1 to 65535, that the isolated network must not be able to reach, such as the production database's. Defaults to none. Refused on a strict policy with no jump host, which has nothing to try them from. On change, the list given replaces the old one, and `[]` clears it.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "jump_host": {
            "description": "Whether a test brings up a jump host on the isolated network. Defaults to true.",
            "type": "boolean"
          },
          "jump_host_flavor": {
            "description": "The flavor the jump host uses. Defaults to the service's own probe flavor.",
            "type": "string"
          },
          "jump_host_image": {
            "description": "The image the jump host boots from. Defaults to the service's own probe image.",
            "type": "string"
          },
          "max_duration": {
            "description": "The longest a test may run, as a duration from `10m` to `168h`, such as `2h`. Defaults to `1h`.",
            "type": "string"
          },
          "name": {
            "description": "The policy's name, unique on this service. Required on create.",
            "type": "string"
          },
          "network_cidrs": {
            "description": "The address ranges the isolated network's subnets are allocated from, each a network address in CIDR form such as `10.250.0.0/24`, with no host bits set. At least one is required. On change, the list given replaces the old one.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "permitted_checks": {
            "description": "The health check types a test may run, `tcp` and `http`. Defaults to both. On change, the list given replaces the old one.",
            "items": {
              "enum": [
                "tcp",
                "http"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "project_id": {
            "description": "The operator project every test under this policy is built in, never a tenant's. Required on create. It must exist here and be enabled.",
            "type": "string"
          },
          "strict": {
            "description": "When true, the copies may send traffic only to addresses inside the isolated network. Defaults to true.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "BucketRef": {
        "description": "One bucket a backup holds, and where its object map is kept.",
        "properties": {
          "bucket": {
            "description": "The bucket's name.",
            "type": "string"
          },
          "diff_provenance": {
            "description": "How the service decided what had changed since the previous backup. `full_read`: there was no previous backup, so every object was read. `s3_object_version`: from object versions. `s3_list_etag`: by comparing each object's etag, size and modification time.",
            "enum": [
              "full_read",
              "s3_object_version",
              "s3_list_etag"
            ],
            "type": "string"
          },
          "gateway": {
            "description": "The object gateway the bucket was read through. For the record only; a restore uses whichever gateway the target cloud has. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "logical_bytes": {
            "description": "The total size of those objects, in bytes.",
            "format": "int64",
            "type": "integer"
          },
          "manifest_digest": {
            "description": "A digest of the object map, checked when it is read back. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "manifest_key": {
            "description": "Where the bucket's object map is kept in the repository. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "manifest_version": {
            "description": "The repository's version id of the object map, where the repository keeps versions. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "object_count": {
            "description": "How many objects the backup holds.",
            "format": "int64",
            "type": "integer"
          },
          "tenant": {
            "description": "The object gateway namespace that holds the bucket, on a gateway that gives each project a namespace of its own, named after the project's id. Absent where buckets share one flat namespace.",
            "type": "string"
          },
          "versioned": {
            "description": "Whether the bucket had versioning on when it was read.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "CapabilitiesResponse": {
        "description": "What this cloud offers the caller's project, so a form can stop offering what plan creation would refuse.",
        "properties": {
          "dr": {
            "description": "True when disaster recovery is enabled on this cloud and at least one of its operations can run here: a recover, a test or a release. False otherwise, and then every disaster-recovery operation answers `dr_not_enabled`. `dr_reason` says which run.",
            "type": "boolean"
          },
          "dr_reason": {
            "description": "What disaster recovery runs on this cloud and what does not, in sentences that can be shown as they are: whether it is enabled here; which of recover, test, release, failback, resync and retire run, and what this cloud lacks for one that does not; whether `hot` groups run here, read from whether a block storage pool of this cloud mirrors to a peer, and naming the pool and the peer; whether a `hot` group can be tested here, which needs a storage client library that clones a snapshot by its id; and what does not run in this release.",
            "type": "string"
          },
          "encryption": {
            "description": "True when a plan may ask for its backups to be encrypted under the workload's own key. False when this cloud runs without a key manager. A plan asking for encryption is then refused.",
            "type": "boolean"
          },
          "immutability": {
            "description": "True when at least one repository on this cloud keeps backups under an immutability lock. It says the cloud offers it, not that this project's policies use it.",
            "type": "boolean"
          },
          "policies": {
            "description": "How many active protection policies the project can choose: public ones, and those assigned to the project or to its domain.",
            "type": "integer"
          },
          "protectable_kinds": {
            "description": "The kinds of resource a plan may name on this cloud: `volume` a block storage volume; `instance` a server with its disks; `bucket` an object storage bucket. A kind is left out when this cloud cannot inspect it.",
            "items": {
              "enum": [
                "volume",
                "instance",
                "bucket"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "protection": {
            "description": "True when the project has at least one protection policy it can choose for a new plan.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "CatalogueCursor": {
        "description": "How far the last read of one dedup domain in the repository got, and what stopped it.",
        "properties": {
          "cursor_at": {
            "description": "The newest object modification time accepted so far. The zero time when nothing has been read yet.",
            "format": "date-time",
            "type": "string"
          },
          "dedup_domain": {
            "description": "The dedup domain: the set of backups in the repository whose data deduplicates against each other, and never against data outside it. It is tied to the encryption choice of the cloud that wrote it. Unencrypted backups share one domain, and backups under a key of their own each have one. This cloud can read a domain only with its key.",
            "type": "string"
          },
          "kek_ref": {
            "description": "The key reference that would open the domain, as its writer published it. Absent when not published.",
            "type": "string"
          },
          "kms": {
            "description": "The key manager holding that key, as its writer published it. Absent when not published.",
            "type": "string"
          },
          "last_error": {
            "description": "Why the last read failed or could not open the domain. Absent when it did not.",
            "type": "string"
          },
          "last_pass_at": {
            "description": "When the domain was last read. The zero time when it never was.",
            "format": "date-time",
            "type": "string"
          },
          "needs_key": {
            "description": "True when this cloud holds no key for the domain; importing its key makes it readable.",
            "type": "boolean"
          },
          "origin_cloud": {
            "description": "The cloud that wrote the domain, as it published it. Absent when not published.",
            "type": "string"
          },
          "points_seen": {
            "description": "How many backups the reads of this domain have found.",
            "format": "int64",
            "type": "integer"
          },
          "repository_id": {
            "description": "The repository the dedup domain is in.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "Check": {
        "description": "One step of a repository health check.",
        "properties": {
          "latency_ns": {
            "description": "How long the step took, in nanoseconds.",
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "description": "The step. `list` lists the repository; `put`, `head`, `get` and `delete` write, stat, read back and remove a small test object; `object_lock` confirms the storage still enforces object lock; `no_default_retention` fails when the bucket locks every object by default; `version_listing`, `conditional_put` and `checksum_enforced` test storage features a repository with object lock relies on.",
            "enum": [
              "list",
              "put",
              "head",
              "get",
              "delete",
              "object_lock",
              "no_default_retention",
              "version_listing",
              "conditional_put",
              "checksum_enforced"
            ],
            "type": "string"
          },
          "ok": {
            "description": "Whether the step succeeded.",
            "type": "boolean"
          },
          "reason": {
            "description": "The storage's own error when the step failed. Absent when it succeeded.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CopyLadderReq": {
        "description": "A copy tier's retention ladder. It reads like the policy's `keep`, and its weeks, months and years start where the policy's anchors say. A ladder that keeps nothing is refused.",
        "properties": {
          "all": {
            "description": "How long every copy is kept, as a duration such as `720h`.",
            "type": "string"
          },
          "daily": {
            "description": "How many days the copy of the first backup of each day is kept.",
            "type": "integer"
          },
          "monthly": {
            "description": "How many months the copy of the first backup of each month is kept.",
            "type": "integer"
          },
          "weekly": {
            "description": "How many weeks the copy of the first backup of each week is kept.",
            "type": "integer"
          },
          "yearly": {
            "description": "How many years the copy of the first backup of each year is kept.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "CopyTier": {
        "description": "A second repository every backup is copied to.",
        "properties": {
          "keep": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Ladder"
              }
            ],
            "description": "How long copies are kept there. Its anchors are always the policy's own."
          },
          "repository_id": {
            "description": "The repository's id.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CopyTierReq": {
        "description": "A second repository every backup is copied to.",
        "properties": {
          "keep": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CopyLadderReq"
              }
            ],
            "description": "How long copies are kept. Required. Its periods start where the policy's keep anchors say."
          },
          "repository_id": {
            "description": "The id of a registered chunk repository other than the backup repository.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CoverageMinimum": {
        "description": "The minimum protection a cloud expects of every policy.",
        "properties": {
          "keep_all": {
            "description": "The shortest time a policy may keep every backup, in nanoseconds. Absent when not set.",
            "format": "int64",
            "type": "integer"
          },
          "requires_lock": {
            "description": "True when every policy must write to a repository that keeps backups under an object lock.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "CoverageProposal": {
        "description": "The account a refusal carries when some disks cannot be protected: what would be protected, what would not, and the digest that identifies it.",
        "properties": {
          "digest": {
            "description": "The digest of the disks with data that would be left out. On a plan refusal, send it back as `partial_coverage.digest`, with `accept` set to `true`, to accept this account. On a restore refusal it identifies the backup's missing disks. Repeat the restore with `acknowledge_partial` set to `true` instead.",
            "type": "string"
          },
          "resources": {
            "description": "One entry per resource: each resource a plan names, or the one resource of a backup. Each lists the disks that would be protected or restored, the disks that would be left out and why, and whether that makes the resource partial.",
            "items": {
              "$ref": "#/components/schemas/ResourceCoverageView"
            },
            "type": "array"
          },
          "scope": {
            "description": "`partial` when at least one disk with data would be left out, otherwise `full`.",
            "enum": [
              "full",
              "partial"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "CoverageResponse": {
        "description": "The coverage report, per project and in total.",
        "properties": {
          "generated_at": {
            "description": "When the report was produced.",
            "format": "date-time",
            "type": "string"
          },
          "minimum": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageMinimum"
              }
            ],
            "description": "The minimum protection this cloud expects of every policy. Absent when it sets none."
          },
          "policies_below_minimum": {
            "description": "The active and deprecated policies that fall below `minimum`, by name. Absent when none does, or when the cloud sets no minimum.",
            "items": {
              "$ref": "#/components/schemas/PolicyNoteView"
            },
            "type": "array"
          },
          "projects": {
            "description": "One entry per project that has a plan or a finding, in order of project id. Empty when no project has either.",
            "items": {
              "$ref": "#/components/schemas/ProjectCoverage"
            },
            "type": "array"
          },
          "totals": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageTotals"
              }
            ],
            "description": "The figures summed over `projects`."
          },
          "unavailable": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Each part of the report that could not be produced, mapped to the reason. Only `unprotected_resources` can appear. Absent when nothing is missing.",
            "type": "object"
          },
          "window": {
            "description": "How far back missed backups are counted, in nanoseconds; 7 days unless the operator set another.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "CoverageTotals": {
        "description": "The coverage report's figures, summed over its projects.",
        "properties": {
          "groups_without_policy": {
            "description": "How many protection groups are promised nothing, listed per project under the same name.",
            "type": "integer"
          },
          "missed_ticks": {
            "description": "How many scheduled backups were missed in the window.",
            "type": "integer"
          },
          "orphaned_points": {
            "description": "How many live backups have no plan and no expiry: a purge could not read the policy that would have dated them, so nothing will expire them automatically.",
            "type": "integer"
          },
          "plans": {
            "description": "How many plans there are.",
            "type": "integer"
          },
          "plans_on_deprecated_policy": {
            "description": "How many plans run under a deprecated policy.",
            "type": "integer"
          },
          "plans_paused": {
            "description": "How many plans are paused.",
            "type": "integer"
          },
          "plans_unsatisfied": {
            "description": "How many plans run under a policy that cannot run backups right now.",
            "type": "integer"
          },
          "plans_without_policy": {
            "description": "How many plans name a policy that no longer exists.",
            "type": "integer"
          },
          "point_objects_owed": {
            "description": "How many committed backups have a point object the manager has not completed yet, listed per project as `point_objects_owed`.",
            "type": "integer"
          },
          "points_awaiting_copy": {
            "description": "How many backups have not yet reached their policy's copy repository.",
            "type": "integer"
          },
          "points_not_immutably_protected": {
            "description": "How many backups are not under an object lock for their whole life.",
            "type": "integer"
          },
          "policies_below_minimum": {
            "description": "How many active or deprecated policies fall below the cloud's minimum.",
            "type": "integer"
          },
          "projects_over_quota": {
            "description": "How many projects are over a quota.",
            "type": "integer"
          },
          "quotas_below_locked_footprint": {
            "description": "How many projects have a stored-bytes quota below what their locked backups occupy.",
            "type": "integer"
          },
          "retired_under_mandatory": {
            "description": "How many resources were taken out of plans under a mandatory policy while their backups are still kept.",
            "type": "integer"
          },
          "unprotected_resources": {
            "description": "How many resources no plan names, when they were listed.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "CoverageView": {
        "description": "What a plan or a backup claims to cover of its resource. On a plan it is the accepted account. On a backup it is what the backup found.",
        "properties": {
          "accepted_at": {
            "description": "On a plan, when the account was accepted or the owner last chose which disks to leave out. Absent on a full plan and on a backup.",
            "format": "date-time",
            "type": "string"
          },
          "accepted_by": {
            "description": "On a plan, the Keystone id of the user who accepted the account.",
            "type": "string"
          },
          "digest": {
            "description": "On a plan, the digest of the accepted account of left-out disks. Absent on a full plan and on a backup.",
            "type": "string"
          },
          "excluded": {
            "description": "The disks left out. On a backup this includes disks a restore rebuilds by itself, which do not make the scope partial. On a plan it lists only disks with data. Absent when there are none.",
            "items": {
              "$ref": "#/components/schemas/ExclusionView"
            },
            "type": "array"
          },
          "scope": {
            "description": "`full` when every disk is included, apart from disks a restore rebuilds by itself. `partial` when at least one disk with data is left out, whoever decided it.",
            "enum": [
              "full",
              "partial"
            ],
            "type": "string"
          },
          "statement": {
            "description": "A backup's own sentence about what it holds and what it does not. Absent on a plan.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CoveredDiskView": {
        "description": "A disk that would be protected.",
        "properties": {
          "device": {
            "description": "The device the guest sees the disk as, such as `/dev/vdb`. Absent when not known.",
            "type": "string"
          },
          "locator": {
            "description": "The value `excluded_disks` takes to name this disk.",
            "type": "string"
          },
          "resource_id": {
            "description": "The volume's id for a volume, the server's id for a disk the server owns.",
            "type": "string"
          },
          "role": {
            "description": "`root` for a server's root disk, `ephemeral` for an ephemeral disk the server owns, `volume` for a block storage volume.",
            "enum": [
              "root",
              "ephemeral",
              "volume"
            ],
            "type": "string"
          },
          "size_bytes": {
            "description": "The disk's size in bytes; 0 when it is known only once the first backup reads the disk.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "CreateAttachmentReq": {
        "description": "An attachment of one disk of a backup to a running server.",
        "properties": {
          "device": {
            "description": "The device name to ask for in the guest, for example `/dev/vdb`. The hypervisor may choose another.",
            "type": "string"
          },
          "expires_in": {
            "description": "How long the attachment stays once it is attached before it is detached and cleaned up, as a duration such as `30m` or `4h`. 24 hours when left out.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup to attach. Its state must be `committed` or `verified`, and it must hold disks.",
            "type": "string"
          },
          "resource_id": {
            "description": "Which disk of the backup, by the id in its `disks` list. The root disk when left out, or the first disk when there is none.",
            "type": "string"
          },
          "target_instance_id": {
            "description": "The running server to attach it to. Required.",
            "type": "string"
          },
          "tier": {
            "description": "The copy to attach from. Attachments read the repository copy, `chunk`, which is also what is read when `tier` is left out. Naming `rbd`, the instant tier, is refused with `tier_unavailable`.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreateBindingReq": {
        "description": "A resource from another cloud to bind to a project here.",
        "properties": {
          "origin_cloud": {
            "description": "The cloud that wrote the backups, as they name it. The inventory lists it. Required.",
            "type": "string"
          },
          "repository_id": {
            "description": "The id of the repository the backups are read from. Required. It must be a chunk repository.",
            "type": "string"
          },
          "source_project": {
            "description": "The resource's project in that cloud, for the record.",
            "type": "string"
          },
          "source_resource_id": {
            "description": "The resource's id in that cloud. Required. A resource can be bound once.",
            "type": "string"
          },
          "standby_pool": {
            "description": "A pool to keep a standby in. When set, a standby is registered once the first backup is adopted, and kept up to date after that. The pool must hold standbys only.",
            "type": "string"
          },
          "standby_prefix": {
            "description": "Put before each disk's id to name the standby's images. Needs `standby_pool`.",
            "type": "string"
          },
          "standby_site_id": {
            "description": "The id of the site to keep a standby at. Defaults to this site.",
            "type": "string"
          },
          "target_domain_id": {
            "description": "The target project's domain. Taken from the project when the service can look it up, and must match it if given; required when it cannot.",
            "type": "string"
          },
          "target_project_id": {
            "description": "The project here to adopt the backups into. Required. When the service can look the project up, it must exist and be enabled.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreateGroupReq": {
        "description": "A new protection group, or, with `recovery_of`, the recovery here of a group another region created. A group runs at the site this service runs at, and a field this request does not list is refused with `invalid_request`. `recovery_of`, `target_project_id`, `target_domain_id`, `bubble_policy_id`, `test_every` and `standby` belong to a recovery and are refused on a new group. `mapping_profile_id` and `accept_gaps` are taken by both: a new group's apply to a failback that rebuilds a member whose original is gone.",
        "properties": {
          "accept_gaps": {
            "description": "Mapping gaps a recovery may proceed with, such as `network:prod-net-b`, or an element alone for every source. Each executes as this cloud's default: an availability zone or volume type is left to the cloud, an unmapped network's port and a floating IP are left out, and a security group is left off the member's ports. An accepted flavor boots on the nearest flavor at least as large as the point recorded. Readiness, tests and recovers evaluate the same acceptance. Empty entries are refused and duplicates dropped. On a new group they apply to a failback that rebuilds a member whose original is gone.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "bubble_policy_id": {
            "description": "The test policy a recovery's tests run under, from GET /v1/bubble-policies.",
            "type": "string"
          },
          "mapping_profile_id": {
            "description": "The recovery profile a recover resolves flavors, networks, zones and volume types through, from GET /v1/recovery-profiles. On a new group it is the failback's, for a member it rebuilds, and naming one is an operator's act: a profile decides where a workload lands.",
            "type": "string"
          },
          "members": {
            "description": "The servers in a new group. At least one is required. A recovery takes its members from the group's document and refuses this field.",
            "items": {
              "$ref": "#/components/schemas/MemberReq"
            },
            "type": "array"
          },
          "name": {
            "description": "The group's name, unique within the project. Required for a new group. For a recovery, defaults to the name in the group's document.",
            "type": "string"
          },
          "network_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Where each network lands at the disaster recovery site: the source network's name, as captured with the server, to the name of a network at that site.",
            "type": "object"
          },
          "policy_id": {
            "description": "The protection policy the group is promised under, from GET /v1/policies. Required. It must state a recovery section, or the request is refused with `policy_has_no_recovery`. For a new group every member must be protected by a plan under it, or the request is refused with `member_not_under_group_policy` naming each member that is not. For a recovery it is this region's own policy, whose recovery site is the group's home.",
            "type": "string"
          },
          "recovery_of": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RecoveryOfReq"
              }
            ],
            "description": "The group to register a recovery of, as GET /v1/repositories/{id}/inventory?view=groups lists it. Operator only. The recovery takes its members, custody epoch and source project from the group's newest document, and binds every member to `target_project_id`."
          },
          "standby": {
            "allOf": [
              {
                "$ref": "#/components/schemas/StandbyReq"
              }
            ],
            "description": "Where each member's standby is kept. Required when the group's policy promises the `warm` rung, and refused otherwise."
          },
          "target_domain_id": {
            "description": "The target project's domain. Needed only when the project cannot be looked up here; when it can, it must match.",
            "type": "string"
          },
          "target_project_id": {
            "description": "The project here a recovery's members are adopted into. Required with `recovery_of`.",
            "type": "string"
          },
          "test_every": {
            "description": "How often a recovery is to be tested, as a duration such as `720h`. Defaults to the cadence the group's home promises. `0s` sets none.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreatePlanReq": {
        "description": "The body that creates a plan.",
        "properties": {
          "consistency": {
            "description": "How each backup is taken. `crash` snapshots the disks as they are, like pulling the power. `filesystem` asks the guest agent to freeze its filesystems around the snapshot, and falls back to `crash` when the guest cannot freeze. Absent or an empty string takes the cloud's default, normally `crash`. Not accepted for a bucket plan.",
            "enum": [
              "",
              "crash",
              "filesystem"
            ],
            "type": "string"
          },
          "enabled": {
            "description": "Whether the plan starts taking scheduled backups at once. Defaults to `true`.",
            "type": "boolean"
          },
          "encryption": {
            "description": "How the plan's data is encrypted, decided once at creation. `barbican` gives the plan an encryption key of its own in the key manager, so its data is deduplicated only with itself. `none` stores it unencrypted, deduplicated with every other unencrypted plan. Absent means the repository's own key, shared with the other plans that left it out. Absent and `barbican` are both refused on a cloud with no key manager; GET /v1/capabilities says whether there is one.",
            "enum": [
              "barbican",
              "none"
            ],
            "type": "string"
          },
          "excluded_disks": {
            "description": "Disks of the named resources the owner chooses not to protect, for example a large scratch volume attached to a server. Each must match exactly one disk. A resource cannot have every disk excluded. Excluded disks are left out of every backup and a restore does not bring them back.",
            "items": {
              "$ref": "#/components/schemas/DiskSelectorReq"
            },
            "type": "array"
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Free-form key and value pairs copied onto every backup the plan writes. The service acts on none of them. At most 64; a key is a name of 1 to 63 characters, optionally after a prefix and a `/`; a value is at most 253 characters and may be empty; names and values are letters and digits with `-`, `_` or `.` inside. Keys under the service's own prefixes are refused.",
            "type": "object"
          },
          "name": {
            "description": "The plan's name, unique within the project. Required; surrounding spaces are removed.",
            "type": "string"
          },
          "partial_coverage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PartialCoverageReq"
              }
            ],
            "description": "Accepts an account of disks that cannot be protected. Send it only after a `partial_coverage_unaccepted` refusal, with the digest that refusal returned. Absent means no partial protection is accepted."
          },
          "policy_id": {
            "description": "The id of the protection policy the plan runs under, one GET /v1/policies lists for this project and that is `active`. Required.",
            "type": "string"
          },
          "resource_ids": {
            "description": "The resources to protect, all of `resource_kind` and all owned by the token's project: volume ids, server ids, or for a bucket plan the bucket's name. Required, without repeats. A bucket plan takes exactly one bucket.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "resource_kind": {
            "description": "What the plan protects. `volume` is a block storage volume. `instance` is a server: its root disk, its ephemeral disks and every attached volume, snapshotted together. `bucket` is an object storage bucket, offered only on a cloud with an object gateway. Required.",
            "enum": [
              "volume",
              "instance",
              "bucket"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreatePointReq": {
        "description": "A manual backup. Either name `plan_id` to run a plan now, or name `resource_kind`, `resource_id` and a vault to back up one server or volume.",
        "properties": {
          "consistency": {
            "description": "Without `plan_id`, `crash` or `filesystem`. The site's default when left out. When the resource is backed up in a plan it must match the plan's level, or be left out.",
            "enum": [
              "crash",
              "filesystem"
            ],
            "type": "string"
          },
          "force_full": {
            "description": "Read every block of the source instead of only what changed since the last backup. Every backup is complete either way; this re-reads the source in case its change tracking is stale.",
            "type": "boolean"
          },
          "keep_until": {
            "description": "Keep the backup until this RFC 3339 time. It must fall within the bounds the vault allows for manual backups, give or take an hour, and is moved inside them. When left out, a backup in a plan is kept for the shortest time allowed. A backup named by `policy_id` is kept for that policy's scheduled retention. A backup into `vault_id` must name `keep_until` or `until_deleted`.",
            "format": "date-time",
            "type": "string"
          },
          "plan_id": {
            "description": "Run this plan now, under its own policy and consistency, into its vault. With it, `resource_kind`, `resource_id`, `vault_id`, `policy_id` and `consistency` must be left out.",
            "type": "string"
          },
          "policy_id": {
            "description": "Without `plan_id` and instead of `vault_id`: the backup goes into the vault this policy keeps its backups in, and the policy's scheduled retention is the default keep.",
            "type": "string"
          },
          "resource_id": {
            "description": "Without `plan_id`, the server or volume to back up, in the token's project. When the resource is in exactly one plan that keeps its backups in the chosen vault, the backup is taken in that plan.",
            "type": "string"
          },
          "resource_ids": {
            "description": "With `plan_id`, back up only these of the plan's resources. Every resource the plan protects when left out.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "resource_kind": {
            "description": "Without `plan_id`, what `resource_id` names. A bucket is backed up by a plan only.",
            "enum": [
              "instance",
              "volume"
            ],
            "type": "string"
          },
          "until_deleted": {
            "description": "Keep the backup until somebody deletes it. Only where the vault offers it, and not together with `keep_until`.",
            "type": "boolean"
          },
          "vault_id": {
            "description": "Without `plan_id`, the vault to keep the backup in. GET /v1/vaults lists the vaults this project is offered.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreatePolicyReq": {
        "description": "The body of a policy creation.",
        "properties": {
          "assigned_to": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AssignmentReq"
              }
            ],
            "description": "The domains and projects to offer the policy to. Needs the `policy:assign` permission too."
          },
          "description": {
            "description": "Free text shown with the policy.",
            "type": "string"
          },
          "mandatory": {
            "description": "`true` means a project cannot pause or delete protection under this policy. Only an operator can. Defaults to `false`.",
            "type": "boolean"
          },
          "name": {
            "description": "The policy's name, unique across the cloud. Required.",
            "type": "string"
          },
          "spec": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PolicySpecReq"
              }
            ],
            "description": "The policy's terms. `spec.schedule`, `spec.keep` and `spec.tiers.backup` are required."
          },
          "state": {
            "description": "`active`, the default, lets new plans name the policy. `deprecated` creates it closed to new plans. `retired` is refused: a policy is retired with POST /v1/policies/{id}/retire.",
            "enum": [
              "active",
              "deprecated"
            ],
            "type": "string"
          },
          "visibility": {
            "description": "`public` offers the policy to every project. `assigned` offers it only to the domains and projects in `assigned_to`. Defaults to `assigned`.",
            "enum": [
              "public",
              "assigned"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreateRepositoryReq": {
        "description": "A repository to register. Which fields apply depends on `kind` and `driver`, and a field that does not apply is refused.",
        "properties": {
          "access_key": {
            "description": "The S3 access key the backup workers use for this repository. Only for the `s3` driver, and only together with `secret_key`. It is sealed by the key manager and never returned. Without it the workers use the credentials of their own environment.",
            "type": "string"
          },
          "bucket": {
            "description": "Required for the `s3` driver, and for it only. The bucket backups are written to.",
            "type": "string"
          },
          "ceph_conf": {
            "description": "The path of the Ceph configuration file on the backup workers, for an rbd tier or the `rados` driver only.",
            "type": "string"
          },
          "ceph_user": {
            "description": "The Ceph user the backup workers authenticate as, for an rbd tier or the `rados` driver only. Defaults to the workers' own.",
            "type": "string"
          },
          "chunk_size": {
            "description": "The chunk size in bytes: a power of two from 65536 (64 KiB) to 67108864 (64 MiB). Defaults to 4194304 (4 MiB). It cannot change later. For chunk repositories only.",
            "format": "int64",
            "type": "integer"
          },
          "driver": {
            "description": "Required for a chunk repository and refused for an rbd tier. `s3` stores into a bucket, `rados` into a Ceph pool, `posix` onto a filesystem.",
            "enum": [
              "s3",
              "rados",
              "posix"
            ],
            "type": "string"
          },
          "endpoint": {
            "description": "The S3 endpoint URL, such as `https://s3.example.net`. For the `s3` driver only.",
            "type": "string"
          },
          "environment_credentials": {
            "description": "For a chunk repository at another site: `true` registers it without first listing it from here, because this service's and the backup workers' own environment supplies the credential. Without it, a repository at another site is listed with the access keys given, or with none, before it is saved, and one that cannot be listed is refused with `repository_unreadable`. Not together with `access_key`.",
            "type": "boolean"
          },
          "kind": {
            "description": "Required. `chunk` for deduplicated backup storage reached through a driver. `rbd` for an instant tier, which keeps recent backups as Ceph images that restores can be cloned from.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          },
          "name": {
            "description": "Required. The repository's name, unique in this cloud.",
            "type": "string"
          },
          "namespace": {
            "description": "For an rbd tier only, the RBD namespace its images are created in.",
            "type": "string"
          },
          "object_lock": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LockReq"
              }
            ],
            "description": "The retention contract the bucket enforces. Only for the `s3` driver, and the bucket itself must have object lock enabled. Health checks verify that it does."
          },
          "pool": {
            "description": "Required for an rbd tier and for the `rados` driver, and for them only. The Ceph pool.",
            "type": "string"
          },
          "prefix": {
            "description": "Required for the `posix` driver, as the filesystem root. For the `s3` and `rados` drivers, an optional key prefix inside the bucket or pool. Not for an rbd tier.",
            "type": "string"
          },
          "reachable_from_sites": {
            "description": "The ids of other sites whose backup workers can also reach this repository.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "region": {
            "description": "The S3 region. For the `s3` driver only.",
            "type": "string"
          },
          "replica_of": {
            "description": "The id of the repository this one is a replica of.",
            "type": "string"
          },
          "secret_key": {
            "description": "The S3 secret key, together with `access_key`. Sealed and never returned.",
            "type": "string"
          },
          "site_name": {
            "description": "Required. The site the repository is local to, by name. A site that does not exist yet is created.",
            "type": "string"
          },
          "soft_delete_grace": {
            "description": "How long a deleted backup or plan stays recoverable, as a duration such as `336h`. Omit it for no grace. For chunk repositories only.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreateRestoreReq": {
        "description": "A restore of one backup. `mode` decides whether it builds something new or overwrites the original. The other fields shape what is built.",
        "properties": {
          "acknowledge_partial": {
            "description": "Set to `true` to restore a backup that does not contain every disk of its resource. Such a restore is refused until it is, and the refusal lists what is missing.",
            "type": "boolean"
          },
          "availability_zone": {
            "description": "The availability zone for new volumes and servers. The source's when left out.",
            "type": "string"
          },
          "confirm_unresolved": {
            "description": "The elements `mapping_profile` leaves unresolved that you accept as they are, by name, as the recovery binding dry run lists them.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "flavor": {
            "description": "For `mode` `instance` only: the flavor for the new server, by name or id. The backup's recorded flavor when left out.",
            "type": "string"
          },
          "key_name": {
            "description": "For `mode` `instance` only: the key pair for the new server.",
            "type": "string"
          },
          "mapping_profile": {
            "description": "The name of a saved recovery profile that translates the backup's flavors, networks, volume types and zones into this cloud's. It is resolved when the request is accepted. Anything it cannot resolve must be named in `confirm_unresolved`.",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Metadata to set on new volumes, on top of the source volume's own.",
            "type": "object"
          },
          "mode": {
            "description": "Required. `volume`: new volumes from the backup's disks. `instance`: a new server, with its disks, built to the backup's recorded shape. `in_place`: write the backup back over the volume or server it was taken from. `bucket`: a new bucket from a bucket backup.",
            "enum": [
              "volume",
              "instance",
              "in_place",
              "bucket"
            ],
            "type": "string"
          },
          "name": {
            "description": "The name of what is created. Volumes from a backup of several disks get `-0`, `-1` and so on appended. When left out, a server keeps the name it was backed up with, volumes get a name built from the backup's id, and a bucket gets a new name that is never the source's.",
            "type": "string"
          },
          "networks": {
            "description": "For `mode` `instance` only: networks for the new server, by name or id. Name one for every port, or one per port in the order the backup recorded them. The recorded networks when left out.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "point_id": {
            "description": "The backup to restore. Required.",
            "type": "string"
          },
          "resource_ids": {
            "description": "For `mode` `volume` or `instance` only: restore only these disks of the backup, by the ids in its `disks` list.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "security_groups": {
            "description": "For `mode` `instance` only: security groups for the new server, by name or id. The recorded groups when left out.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "target_project_id": {
            "description": "Restore into this project instead of the backup's own. Operators only. The crossing is audited.",
            "type": "string"
          },
          "tier": {
            "description": "The copy to restore from. `chunk`: the repository copy. `rbd`: the instant tier, which clones each disk in seconds and hands the volumes to the project. It serves `volume` and `instance` restores of backups made only of Cinder volumes, from a tier at this site, when no `volume_type`, `availability_zone` or `mapping_profile` is named; an `instance` restore also needs the repository copy, which describes the server. Left out, the faster copy that can serve the request is read. Naming one that cannot is refused with `tier_unavailable`, saying why. GET /v1/restores/estimate shows each copy, whether it can be read and why not.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          },
          "volume_type": {
            "description": "The volume type for new volumes. The source volume's type when left out.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreateSessionReq": {
        "description": "A sign-in with a user name and password. Leave the project out to get an unscoped token and the projects it can be scoped to.",
        "properties": {
          "password": {
            "description": "The user's password. Required. It is passed to the cloud's identity service for this request only, and never stored or logged.",
            "type": "string"
          },
          "project": {
            "description": "The name of the project to scope the token to. Leave it and `project_id` out for an unscoped token.",
            "type": "string"
          },
          "project_domain": {
            "description": "The name of the domain `project` belongs to. The user's domain when left out. Used only with `project`.",
            "type": "string"
          },
          "project_id": {
            "description": "The id of the project to scope the token to. Wins over `project` when both are given.",
            "type": "string"
          },
          "user": {
            "description": "The user's name. Required.",
            "type": "string"
          },
          "user_domain": {
            "description": "The name of the user's domain. `Default` when left out.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreateStandbyReq": {
        "description": "A standby to register.",
        "properties": {
          "domain_id": {
            "description": "The domain of that project. Taken from the project when the service can look it up, and must match it if given; required when it cannot.",
            "type": "string"
          },
          "group_id": {
            "description": "The id of a protection group to associate. Must name an existing group in `project_id`. Recorded only.",
            "type": "string"
          },
          "project_id": {
            "description": "The project the standby belongs to, which can read and promote it. Required. When the service can look the project up, it must exist and be enabled.",
            "type": "string"
          },
          "resource_id": {
            "description": "The protected resource to keep a copy of. Required.",
            "type": "string"
          },
          "site_id": {
            "description": "The id of the site to keep the copy at, from GET /v1/sites. Defaults to this site. Only a site where the service runs keeps its copies up to date.",
            "type": "string"
          },
          "target_pool": {
            "description": "The pool to keep the copy in. Required. It must hold standbys only, because every update overwrites what is in it.",
            "type": "string"
          },
          "target_prefix": {
            "description": "Put before each disk's id to name its image, so one pool can hold many standbys.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreateSummary": {
        "description": "The totals of what a recovery would create, as counted from `creates`.",
        "properties": {
          "floating_ips": {
            "description": "How many floating IPs it would allocate from the pool, one per floating IP of the source server. 0 when no pool is set.",
            "type": "integer"
          },
          "instances": {
            "description": "How many servers a recovery would create. 1 for a backup of a server, 0 otherwise.",
            "type": "integer"
          },
          "keypairs": {
            "description": "How many key pairs it would import from the public key the backup captured.",
            "type": "integer"
          },
          "ports": {
            "description": "How many ports it would create, one per network attachment of the source server whose network resolved.",
            "type": "integer"
          },
          "security_groups": {
            "description": "How many security groups it would recreate from their backed-up rules. Groups reused as they stand are not counted.",
            "type": "integer"
          },
          "storage_gb": {
            "description": "The storage it would take, in GiB: every volume, plus each disk of the server that has no volume behind it, such as a local root disk.",
            "format": "int64",
            "type": "integer"
          },
          "volumes": {
            "description": "How many volumes it would create, one per volume in the backup.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "CustodyEpochs": {
        "description": "The custody epochs a disaster recovery run moves a protection group between.",
        "properties": {
          "from": {
            "description": "The group's custody epoch when the run was admitted.",
            "format": "int64",
            "type": "integer"
          },
          "to": {
            "description": "The epoch the run moved the group to at its point of no return. 0 until then.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "DRAction": {
        "description": "One step of a disaster recovery run, as its journal records it.",
        "properties": {
          "attempts": {
            "description": "How many times the step has been started.",
            "type": "integer"
          },
          "error": {
            "description": "Why the last attempt failed, or what a waiting step waits for. Absent otherwise.",
            "type": "string"
          },
          "exec_epoch": {
            "description": "The pick-up of the run that last recorded this step.",
            "format": "int64",
            "type": "integer"
          },
          "finished_at": {
            "description": "When the step finished. Absent until it is done.",
            "format": "date-time",
            "type": "string"
          },
          "member_id": {
            "description": "The group member the step is for. Absent for a step that is not about one member.",
            "type": "string"
          },
          "observed": {
            "description": "What the step found before acting, as the step recorded it. Absent when it recorded nothing."
          },
          "phase": {
            "description": "The part of the operation the step belongs to.",
            "type": "string"
          },
          "result": {
            "description": "What the step produced. Absent until it is done, or when it produced nothing."
          },
          "seq": {
            "description": "The order in which the step first started within the run, from 1.",
            "type": "integer"
          },
          "started_at": {
            "description": "When the step first started.",
            "format": "date-time",
            "type": "string"
          },
          "state": {
            "description": "`running`: started and not yet recorded as finished. `waiting`: waiting for something outside the run. `done`: finished, and skipped if the run is picked up again. `failed`: the last attempt failed; a picked-up run observes what it did before trying again.",
            "enum": [
              "running",
              "waiting",
              "done",
              "failed"
            ],
            "type": "string"
          },
          "step_key": {
            "description": "The step's name, unique within the run.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DRMemberPlan": {
        "description": "What a recover or a test would do for one member.",
        "properties": {
          "absent": {
            "description": "For a resync, true when no server of the member exists in this region, such as one a halted recover never booted: there is nothing to stop and nothing that could have diverged, so the member is passed over and needs no plan. Absent otherwise.",
            "type": "boolean"
          },
          "applied_snap": {
            "description": "For a `warm` group, the snapshot the standby took when it finished applying `point_id`, which is what would be cloned. Absent otherwise.",
            "type": "string"
          },
          "boot_order": {
            "description": "The member's boot tier. A tier boots once the tier before it is healthy, and lower numbers come first.",
            "type": "integer"
          },
          "delta": {
            "description": "For a failback into an original server, true when only what differs from `held_point_id` is written. Absent when every byte is written.",
            "type": "boolean"
          },
          "delta_reason": {
            "description": "For a failback into an original server, why every byte is written. Absent when only the difference is.",
            "type": "string"
          },
          "disks": {
            "additionalProperties": {
              "$ref": "#/components/schemas/ReturnDisk"
            },
            "description": "For a failback into an original server, which of the original's disks each disk of the backup is written into, keyed by the backup's disk id. Absent otherwise.",
            "type": "object"
          },
          "divergence": {
            "description": "For a failback into an original server, how each of its disks compared with the snapshot of `held_point_id`. Absent otherwise.",
            "items": {
              "$ref": "#/components/schemas/DiskDivergence"
            },
            "type": "array"
          },
          "divergence_unverifiable": {
            "description": "For a failback, why some of the original's disks could not be compared; such a member returns only while its original is still locked by the release. Absent when every disk was compared.",
            "type": "string"
          },
          "health": {
            "allOf": [
              {
                "$ref": "#/components/schemas/HealthCheck"
              }
            ],
            "description": "The check the member must pass before the next tier boots."
          },
          "held_point_id": {
            "description": "For a failback, the backup the original server holds exactly. Absent when it holds none.",
            "type": "string"
          },
          "instance_id": {
            "description": "The member's server id in the group's home.",
            "type": "string"
          },
          "known_point_id": {
            "description": "For a resync, the backup the other region recovered the member from, which this region holds; the member is compared with its snapshot to tell whether it diverged. Absent when none is held here.",
            "type": "string"
          },
          "mapping": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RestorePlan"
              }
            ],
            "description": "For a recover, how the member's server would be rebuilt in the group's project: its flavor, networks, volume types and the rest, resolved through `profile_id` and the group's `network_map`. Absent for a test, whose copies land on the test's isolated network."
          },
          "mirror": {
            "description": "For a `hot` group, each mirrored image of the member and what the run does with it. Absent otherwise.",
            "items": {
              "$ref": "#/components/schemas/MirrorDisk"
            },
            "type": "array"
          },
          "name": {
            "description": "The member's display name. Absent when none.",
            "type": "string"
          },
          "overwrite_diverged": {
            "description": "For a failback asked with `overwrite_diverged`, true when this member's original diverged: the run stops and locks it if it is running, keeps what it holds as a divergence point under `plan_id`, then writes every byte of the backup over it. `delta_reason` says how it diverged. Absent otherwise.",
            "type": "boolean"
          },
          "partial": {
            "description": "True when that backup does not hold every disk of the server. Such a member refuses the run with `partial_coverage_unaccepted` unless the group's registration accepts `partial_coverage`. Absent otherwise.",
            "type": "boolean"
          },
          "plan_id": {
            "description": "For a release or a resync, the backup plan that protects the member's server; for a member a failback overwrites, the plan its divergence point is taken under. Absent otherwise.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup the member would come back from.",
            "type": "string"
          },
          "rebuild": {
            "description": "For a failback, true when the original server no longer exists and the member is rebuilt as a new one. Absent otherwise.",
            "type": "boolean"
          },
          "recovery_point_at": {
            "description": "The instant that backup's data is from.",
            "format": "date-time",
            "type": "string"
          },
          "return_to_original": {
            "description": "For a failback, true when the backup is written back into the original server, in place. Absent otherwise.",
            "type": "boolean"
          },
          "running_instance_id": {
            "description": "For a release, a resync or a retire, the server this region runs the member as; for a failback, the server the recovery region ran it as. Absent otherwise.",
            "type": "string"
          },
          "standby_id": {
            "description": "For a `warm` group, the standby that would be cloned. Absent otherwise.",
            "type": "string"
          },
          "standby_pool": {
            "description": "For a `warm` group, the pool that standby is kept in. Absent otherwise.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DRPlan": {
        "description": "What a disaster recovery run would do, worked out from what this region holds now: for a recover or a test, which backup each member would come back from, in which boot tier, and where; for a release, a resync or a retire, which servers it would act on. A run works its plan out again when it starts, so a run started later can differ from a plan read earlier.",
        "properties": {
          "bubble_policy_id": {
            "description": "For a test, the test policy it would run under. Absent for a recover.",
            "type": "string"
          },
          "bubble_project": {
            "description": "For a test, the operator project its isolated network and copies would be built in. Absent for a recover.",
            "type": "string"
          },
          "custody_epoch_to": {
            "description": "The custody epoch the run would move the group to at its point of no return. Absent when not yet known.",
            "format": "int64",
            "type": "integer"
          },
          "direct": {
            "description": "True when a recover would check health straight from this service, which can reach the project's networks, instead of from a probe server. Absent otherwise.",
            "type": "boolean"
          },
          "direction": {
            "description": "For a recover, `failover` when the workload comes to a recovery registration, or `failback` when it comes back to the group's home. Absent otherwise.",
            "enum": [
              "failover",
              "failback"
            ],
            "type": "string"
          },
          "evidence": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Evidence"
              }
            ],
            "description": "For a resync or a retire, the other region's record of taking the group, which is what allows the run. Absent otherwise."
          },
          "members": {
            "description": "Each member's plan, in boot order.",
            "items": {
              "$ref": "#/components/schemas/DRMemberPlan"
            },
            "type": "array"
          },
          "mode": {
            "description": "For a recover, `planned` or `unplanned`. Absent otherwise.",
            "enum": [
              "planned",
              "unplanned"
            ],
            "type": "string"
          },
          "newest_at": {
            "description": "The instant the data of the member whose data is newest is from. Absent when no member's is known.",
            "format": "date-time",
            "type": "string"
          },
          "nonce": {
            "description": "Never returned.",
            "type": "string"
          },
          "oldest_at": {
            "description": "The instant the data of the member whose data is oldest is from. Absent when no member's is known.",
            "format": "date-time",
            "type": "string"
          },
          "operation": {
            "description": "The operation planned.",
            "enum": [
              "release",
              "recover",
              "test",
              "resync",
              "retire"
            ],
            "type": "string"
          },
          "overwrite_diverged": {
            "description": "For a failback, true when an operator asked it to come home over originals that diverged: each such member is marked `overwrite_diverged`. Absent otherwise.",
            "type": "boolean"
          },
          "plans": {
            "description": "For a release or a resync, the backup plans it would pause. Absent otherwise.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "probe_flavor": {
            "description": "The flavor that server would use. Absent when none is set.",
            "type": "string"
          },
          "probe_image": {
            "description": "The image of the server that would run the health checks from inside the members' network: a recover's probe, or a test's jump host. Absent when none is set.",
            "type": "string"
          },
          "profile_id": {
            "description": "The recovery profile the mapping was resolved through. Absent when the group names none.",
            "type": "string"
          },
          "readiness_overridden": {
            "description": "True when a release goes ahead without a fresh readiness report, by an operator's override. Absent otherwise.",
            "type": "boolean"
          },
          "recovery_point": {
            "description": "How the members' backups were chosen: `aligned` or `newest`, as an unplanned recover's `recovery_point` asked; `release` for a planned recover, which takes exactly the release's final backups; `held` when the originals at home are taken back as they are. A test always takes each member's newest. Empty for a release, a resync or a retire.",
            "enum": [
              "aligned",
              "newest",
              "release",
              "held",
              ""
            ],
            "type": "string"
          },
          "recovery_region": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RemoteReadiness"
              }
            ],
            "description": "For a release, the other region's newest report of whether it could recover every member. Absent when none was read."
          },
          "release": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ReleaseProof"
              }
            ],
            "description": "For a planned recover, the release it verified and acts on. Absent otherwise."
          },
          "rung": {
            "description": "The rung the group's home promises. `cold` restores each member from a backup adopted here; `warm` clones each member's standby.",
            "enum": [
              "cold",
              "warm"
            ],
            "type": "string"
          },
          "skip_divergence_point": {
            "description": "For a resync, true when the operator skips the backup of what diverged. Absent otherwise.",
            "type": "boolean"
          },
          "spread_seconds": {
            "description": "Seconds between `oldest_at` and `newest_at`. 0 when every member would recover to the same instant.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "DRPlanResponse": {
        "description": "The answer to a recover or a test asked with `dry_run=true`: the plan the run would follow if it were started now. Nothing was recorded, queued or changed.",
        "properties": {
          "dry_run": {
            "description": "Always true.",
            "type": "boolean"
          },
          "plan": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DRPlan"
              }
            ],
            "description": "The plan."
          }
        },
        "type": "object"
      },
      "DRResource": {
        "description": "One thing a disaster recovery run created or meant to create, written down before it was created. The record outlives the run and the group, so whatever is marked for teardown is removed even when the run that made it is gone.",
        "properties": {
          "attempts": {
            "description": "How many times its teardown has been attempted.",
            "type": "integer"
          },
          "created_at": {
            "description": "When the record was written.",
            "format": "date-time",
            "type": "string"
          },
          "disposition": {
            "description": "What becomes of it. `teardown`: it is removed when the run ends or its expiry passes. `keep`: it stays. `unmanage`: it is released from block storage management without being deleted. `unlock`: it is unlocked.",
            "enum": [
              "teardown",
              "keep",
              "unmanage",
              "unlock"
            ],
            "type": "string"
          },
          "error": {
            "description": "Why the last teardown attempt failed. Absent otherwise.",
            "type": "string"
          },
          "expires_at": {
            "description": "When it is torn down even if its run has not ended. Absent when it has no expiry.",
            "format": "date-time",
            "type": "string"
          },
          "external_id": {
            "description": "Its id in the service that holds it, once known. Absent until then.",
            "type": "string"
          },
          "group_id": {
            "description": "The protection group the run was for.",
            "type": "string"
          },
          "id": {
            "description": "The record's id.",
            "type": "string"
          },
          "kind": {
            "description": "What sort of thing it is, such as a server, a volume or a network.",
            "type": "string"
          },
          "name": {
            "description": "Its name, chosen by the run before it was created.",
            "type": "string"
          },
          "parent": {
            "description": "What it was made from, such as the snapshot a clone came from. Absent otherwise.",
            "type": "string"
          },
          "ponr_disposition": {
            "description": "The disposition it takes when the run passes its point of no return. Absent when that changes nothing.",
            "enum": [
              "teardown",
              "keep",
              "unmanage",
              "unlock"
            ],
            "type": "string"
          },
          "pool": {
            "description": "The storage pool it lives in, for a disk image. Absent otherwise.",
            "type": "string"
          },
          "project_id": {
            "description": "The project it was created in. Empty when it belongs to no project.",
            "type": "string"
          },
          "run_id": {
            "description": "The run that wrote the record.",
            "type": "string"
          },
          "state": {
            "description": "`intended`: written down, and perhaps not created yet. `created`: the run saw it created. `torn_down`: it has been removed.",
            "enum": [
              "intended",
              "created",
              "torn_down"
            ],
            "type": "string"
          },
          "step_key": {
            "description": "The step that wrote the record.",
            "type": "string"
          },
          "torn_down_at": {
            "description": "When it was removed. Absent until then.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DRRun": {
        "description": "One disaster recovery operation on a protection group: a release, a recover, a test, a resync or a retire. A run is carried out step by step; every step is journalled as an action, and everything a step creates is written to the run's ledger before it is created. In this release unplanned recovers and tests run; a group may also list runs an earlier release recorded, each `cancelled` or `failed` with the reason in `error`.",
        "properties": {
          "abort_requested_at": {
            "description": "When an abort was asked for. Absent if none was.",
            "format": "date-time",
            "type": "string"
          },
          "abort_requested_by": {
            "description": "The id of the operator who asked for the abort. Absent if none was asked for.",
            "type": "string"
          },
          "actions": {
            "description": "The run's journal, in the order its steps first started. Present only when the request asked for `include=actions`.",
            "items": {
              "$ref": "#/components/schemas/DRAction"
            },
            "type": "array"
          },
          "bubble_policy_id": {
            "description": "For a test, the isolated-network policy it runs under. Absent otherwise.",
            "type": "string"
          },
          "cancel_requested_at": {
            "description": "When a cancel was asked for. Absent if none was.",
            "format": "date-time",
            "type": "string"
          },
          "cancel_requested_by": {
            "description": "The id of the user who asked for the cancel. Absent if none was asked for.",
            "type": "string"
          },
          "confirmed_by": {
            "description": "For an unplanned recover, the id of the user who confirmed it. Absent otherwise.",
            "type": "string"
          },
          "custody_epoch": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CustodyEpochs"
              }
            ],
            "description": "The custody epoch the run started at and the one it moves the group to."
          },
          "deadline_at": {
            "description": "When the run must have finished by. Absent when it has no deadline.",
            "format": "date-time",
            "type": "string"
          },
          "direction": {
            "description": "Which way the workload moves, for an operation that moves it. Absent otherwise.",
            "type": "string"
          },
          "error": {
            "description": "Why the run failed, halted or was cancelled, or the last error a still-running run met. Absent otherwise.",
            "type": "string"
          },
          "error_code": {
            "description": "A stable code for why the run failed, halted or was cancelled. Absent otherwise. `isolation_breached`: a test's jump host reached an isolation canary, so no member was booted. `isolation_unproven`: a test's isolated network could not be shown to have no way out, or its jump host did not report on the canaries in time. `health_check_failed`: a member did not pass its health check. `member_boot_failed`: a member's server did not boot. `run_deadline_exceeded`: the run passed `deadline_at`. `run_panicked`: the service failed while carrying the run out. `reprotect_failed`: a recover brought every member up but could not put the recovered servers under a backup plan. `standby_advanced`: a standby moved to a newer backup while a recover was cloning it. Any code a recover or a test can be refused with, such as `mapping_unresolved`, `recovery_delegation_missing` or `member_not_ready`, when the run's own preflight, which checks everything again before acting, found it.",
            "type": "string"
          },
          "exec_epoch": {
            "description": "How many times the run has been picked up to be carried out. Each pick-up takes a new number, and a worker holding an older one can no longer record anything for the run.",
            "format": "int64",
            "type": "integer"
          },
          "finished_at": {
            "description": "When the run ended or halted. Absent while it has not.",
            "format": "date-time",
            "type": "string"
          },
          "group_id": {
            "description": "The protection group the run is for.",
            "type": "string"
          },
          "id": {
            "description": "The run's id.",
            "type": "string"
          },
          "job_id": {
            "description": "The job that carries the run out. A resumed run has a new one.",
            "type": "string"
          },
          "measured": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RunMeasured"
              }
            ],
            "description": "What the run measured. Each figure is absent until it was measured."
          },
          "members": {
            "description": "Each group member's progress through the run, in boot order: the server it became, the backup it was recovered from, and when it was materialised, booted and found healthy. Returned when one run is read; a list of runs leaves it out, and so does a run no step of which has been about a member yet.",
            "items": {
              "$ref": "#/components/schemas/RunMember"
            },
            "type": "array"
          },
          "mode": {
            "description": "For a recover: `planned` acts only on a release the other region completed; `unplanned` acts without one, while the other region may still be running the workload. Absent for other operations.",
            "enum": [
              "planned",
              "unplanned"
            ],
            "type": "string"
          },
          "operation": {
            "description": "`release`: stop the workload here and hand it over. `recover`: bring the workload up here. `test`: bring copies up in an isolated network and tear them down, changing nothing. `resync`: discard this region's divergence after backing it up. `retire`: remove what a superseded recovery left here.",
            "enum": [
              "release",
              "recover",
              "test",
              "resync",
              "retire"
            ],
            "type": "string"
          },
          "options": {
            "description": "The operation's own options, as admitted. Absent when there were none."
          },
          "phase": {
            "description": "The part of the operation the run is in. A recover goes through `preflight`, `materialise`, `commit`, `boot` and `health` tier by tier, `measure`, `record` and `reprotect`; a test through `preflight`, `bubble`, `materialise`, `boot` and `health`, `measure` and `record`. Absent before its first step.",
            "type": "string"
          },
          "ponr_at": {
            "description": "When the run passed its point of no return. Before it, a failure or a cancel removes what the run created; after it, nothing is removed. Absent until then.",
            "format": "date-time",
            "type": "string"
          },
          "prior_state": {
            "description": "The group's state when the run was admitted, which a cancelled or failed run returns it to.",
            "type": "string"
          },
          "reason": {
            "description": "Why the run was started, as the caller gave it. Absent when none was given.",
            "type": "string"
          },
          "release": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ReleaseRef"
              }
            ],
            "description": "The release a recover acts on. Absent for other runs, and until it is found."
          },
          "requested_by": {
            "description": "The id of the user who started the run, or who last resumed it.",
            "type": "string"
          },
          "resources": {
            "description": "The run's ledger: everything the run created or meant to create, and what becomes of each. Present only when the request asked for `include=resources`.",
            "items": {
              "$ref": "#/components/schemas/DRResource"
            },
            "type": "array"
          },
          "resume_count": {
            "description": "How many times the run has been resumed after halting.",
            "type": "integer"
          },
          "rung": {
            "description": "The recovery rung of the group's policy when the run started. Absent when it named none.",
            "enum": [
              "cold",
              "warm",
              "hot"
            ],
            "type": "string"
          },
          "started_at": {
            "description": "When the run was admitted.",
            "format": "date-time",
            "type": "string"
          },
          "state": {
            "description": "`pending`: admitted, not started. `running`: a step is being carried out. `waiting`: the run is waiting for something outside it and will look again; `waiting_reason` says what. `tearing_down`: before its point of no return, the run is removing what it created because it failed or was cancelled. `succeeded`, `failed` and `cancelled` are final. `halted`: the run stopped past its point of no return, because it failed or was aborted, and left everything as it was; it is the one state that can be resumed.",
            "enum": [
              "pending",
              "running",
              "waiting",
              "tearing_down",
              "succeeded",
              "failed",
              "cancelled",
              "halted"
            ],
            "type": "string"
          },
          "updated_at": {
            "description": "When the run's record last changed.",
            "format": "date-time",
            "type": "string"
          },
          "waiting_reason": {
            "description": "What a waiting run waits for. Absent unless the run is waiting.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DRRunPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/DRRun"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "DelegationState": {
        "description": "How a disaster recovery run here would act in the group's project: through a standing delegation a member of the project gave, or through a role an operator granted this service's own user there. A recover and this report use the same check, so readiness never says a group is ready that a recover would refuse, or the reverse.",
        "properties": {
          "armed": {
            "description": "True when a member's delegation exists and has not expired.",
            "type": "boolean"
          },
          "expires_at": {
            "description": "When that delegation expires, or expired. Absent when there has never been one.",
            "format": "date-time",
            "type": "string"
          },
          "via": {
            "description": "The path a run would take: `trust`, the member's delegation; `service_role`, the role this service's user holds in the project. Absent when neither works, which refuses every run with `recovery_delegation_missing`.",
            "enum": [
              "trust",
              "service_role"
            ],
            "type": "string"
          },
          "why": {
            "description": "Why neither path works. Absent when one does.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DeletedView": {
        "description": "The answer to deleting a plan or a backup.",
        "properties": {
          "deletion_id": {
            "description": "When a plan was deleted with its backups, the id that ties them together, so that undeleting the plan brings them back.",
            "type": "string"
          },
          "detail": {
            "description": "What happened and what happens next, in words for people.",
            "type": "string"
          },
          "job_id": {
            "description": "When a backup is erased at once rather than hidden, because its repository keeps no deleted backups and nothing locks it, the job erasing it. Absent otherwise.",
            "type": "string"
          },
          "plan_id": {
            "description": "The deleted plan's id, when a plan was deleted.",
            "type": "string"
          },
          "point_id": {
            "description": "The deleted backup's id, when a backup was deleted.",
            "type": "string"
          },
          "points": {
            "description": "When a plan was deleted with its backups, how many backups were deleted with it.",
            "type": "integer"
          },
          "purge_after": {
            "description": "When the deleted object is removed for good. Until then an operator can undelete it. For a plan deleted with its backups, it is when the last of them is erased. For a backup erased at once, the time of the request.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DiskDivergence": {
        "description": "How one disk of an original server compared with the snapshot of the backup it holds.",
        "properties": {
          "checked": {
            "description": "True when the disk was compared.",
            "type": "boolean"
          },
          "diverged": {
            "description": "True when the disk changed after the snapshot.",
            "type": "boolean"
          },
          "resource_id": {
            "description": "The disk's id.",
            "type": "string"
          },
          "role": {
            "description": "Whether the disk is the server's `root` disk or a `volume`.",
            "type": "string"
          },
          "why": {
            "description": "Why the disk could not be compared. Absent when it was.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DiskExclusion": {
        "description": "One disk, or class of objects, of the resource that a backup does not contain, and why.",
        "properties": {
          "detail": {
            "description": "The reason in words.",
            "type": "string"
          },
          "effect": {
            "description": "What leaving it out costs a restore. `data_loss`: data that will not come back, and a backup with any of these is partial. `regenerated`: something a restore rebuilds by itself, such as a config drive or swap.",
            "enum": [
              "data_loss",
              "regenerated"
            ],
            "type": "string"
          },
          "kind": {
            "description": "What is left out. `root`, `ephemeral`, `swap` and `config_drive` are disks the server owns; `volume` is a block storage volume; `objects` is a class of objects in a bucket.",
            "enum": [
              "root",
              "ephemeral",
              "swap",
              "config_drive",
              "volume",
              "objects"
            ],
            "type": "string"
          },
          "locator": {
            "description": "Tells two excluded disks of one server apart, for example `_disk.eph0` or `_disk.config` for a disk the server owns, or the device, such as `/dev/vdb`, for a volume.",
            "type": "string"
          },
          "name": {
            "description": "The disk's or volume's name, when it has one.",
            "type": "string"
          },
          "reason": {
            "description": "Why it is left out, as a stable code from the same list that explains protection refusals, for example `non_rbd_backend` or `regenerated_at_restore`.",
            "type": "string"
          },
          "resource_id": {
            "description": "The volume's id for a volume; the server's id for a disk the server owns.",
            "type": "string"
          },
          "resource_kind": {
            "description": "The kind of resource the excluded part belongs to.",
            "enum": [
              "instance",
              "volume",
              "bucket"
            ],
            "type": "string"
          },
          "size_bytes": {
            "description": "The excluded disk's size, in bytes, when known.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "DiskRead": {
        "description": "One of the backup's disks as a browse session reads it.",
        "properties": {
          "fetched_bytes": {
            "description": "How many bytes of the disk have been read from the backup so far. Only what the browse touches is fetched, so this is usually a small part of `volume_bytes`.",
            "format": "int64",
            "type": "integer"
          },
          "resource_id": {
            "description": "The volume or server the disk belongs to. Absent when the backup records none.",
            "type": "string"
          },
          "role": {
            "description": "The disk's role in the backup, such as `root` or `volume`. Absent when the backup records none.",
            "type": "string"
          },
          "volume_bytes": {
            "description": "The disk's size, in bytes.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "DiskRef": {
        "description": "One disk a backup holds, the storage it was read from and where its data map is kept.",
        "properties": {
          "backend": {
            "description": "How the disk was read. `rbd`, or absent: directly from the cluster's storage. `libvirt`: through the hypervisor running the server. `block`: by attaching the volume to the service's own worker.",
            "enum": [
              "rbd",
              "libvirt",
              "block"
            ],
            "type": "string"
          },
          "boot_index": {
            "description": "The disk's place in the server's boot order, 0 for the boot disk.",
            "type": "integer"
          },
          "chunk_count": {
            "description": "How many chunks of the disk hold data.",
            "format": "int64",
            "type": "integer"
          },
          "device": {
            "description": "The device name the guest saw the disk under, for example `/dev/vdb`.",
            "type": "string"
          },
          "hole_count": {
            "description": "How many chunks of the disk were never written and hold nothing.",
            "format": "int64",
            "type": "integer"
          },
          "host": {
            "description": "The compute host the disk had to be read on, for a disk only its hypervisor can read. Absent otherwise. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "image": {
            "description": "The storage image the disk was read from.",
            "type": "string"
          },
          "instance_id": {
            "description": "The server a volume was read through, when it was read through its hypervisor. Absent otherwise.",
            "type": "string"
          },
          "manifest_digest": {
            "description": "A digest of the data map, checked when it is read back. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "manifest_key": {
            "description": "Where the disk's data map is kept in the repository. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "manifest_version": {
            "description": "The repository's version id of the data map object, where the repository keeps versions. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "namespace": {
            "description": "The storage namespace inside the pool, when there is one. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "pool": {
            "description": "The storage pool the disk's image lives in.",
            "type": "string"
          },
          "resource_id": {
            "description": "The volume's id for a volume; the server's id for a disk the server owns.",
            "type": "string"
          },
          "role": {
            "description": "`root`: the server's own root disk. `ephemeral`: an extra disk the server's flavor gave it. `volume`: a block storage volume.",
            "enum": [
              "root",
              "ephemeral",
              "volume"
            ],
            "type": "string"
          },
          "size_bytes": {
            "description": "The disk's provisioned size, in bytes.",
            "format": "int64",
            "type": "integer"
          },
          "snap_name": {
            "description": "The storage snapshot the disk was read from. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "target": {
            "description": "The guest's name for a disk read through its hypervisor, for example `vda`.",
            "type": "string"
          },
          "via_snapshot": {
            "description": "True when the volume could not be attached as itself, so the backup read a temporary copy made from a snapshot.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "DiskSelector": {
        "description": "One disk of a plan's resources that its owner chose not to protect.",
        "properties": {
          "locator": {
            "description": "Which disk of the resource, when the owner named one. Absent when the resource id alone names the disk, as for a volume.",
            "type": "string"
          },
          "resource_id": {
            "description": "The resource that has the disk, a volume's id or the server's id.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DiskSelectorReq": {
        "description": "One disk of a plan's resources that the owner does not want protected.",
        "properties": {
          "locator": {
            "description": "Which disk of the resource. Not needed for a volume. For a disk a server owns, the locator from GET /v1/resources or from the coverage account, for example `_disk` for the root disk or `_disk.eph0` for the first ephemeral disk.",
            "type": "string"
          },
          "resource_id": {
            "description": "The resource that has the disk: a volume's id, including a volume attached to a server the plan protects, or the server's id for a disk the server itself owns.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DomainKeyList": {
        "description": "The dedup domains of one repository that this cloud holds keys for.",
        "properties": {
          "domain_keys": {
            "description": "One entry per dedup domain, with its key's fingerprint or why it is unreadable.",
            "items": {
              "$ref": "#/components/schemas/DomainKeyView"
            },
            "type": "array"
          },
          "repository": {
            "description": "The repository's name.",
            "type": "string"
          },
          "repository_id": {
            "description": "The repository's id.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "DomainKeyView": {
        "description": "A dedup domain's key as this cloud holds it, identified by its fingerprint. The key itself is never returned.",
        "properties": {
          "domain": {
            "description": "The dedup domain the key opens: the set of backups in the repository whose data deduplicates against each other. It follows the encryption choice. For example, `default` holds data under the repository's own key, and `w-` with a hash holds the data of one plan with a key of its own.",
            "type": "string"
          },
          "fingerprint": {
            "description": "A keyed hash of the key, 16 hexadecimal characters. Two clouds that hold the same key show the same fingerprint. Empty when the key is unreadable.",
            "type": "string"
          },
          "kek_ref": {
            "description": "What wraps the key in this cloud: the key manager's name, such as `barbican`, `vault` or `local`, or for a domain with a key-encryption key of its own, that key's id in the key manager. Absent when not recorded.",
            "type": "string"
          },
          "unreadable": {
            "description": "Why this cloud cannot unwrap the key, for example because no key manager is configured or the key-encryption key was removed. Absent when the key is readable.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "Entry": {
        "description": "One file, directory or link inside a backup, with the metadata its filesystem recorded.",
        "properties": {
          "gid": {
            "description": "The id of the group that owns the entry.",
            "type": "integer"
          },
          "link_target": {
            "description": "For a symbolic link, the path it points to, as written in the link. Absent for anything else, or when it could not be read.",
            "type": "string"
          },
          "mode": {
            "description": "The permission bits, without the file type, as a number. 420 is octal 0644.",
            "minimum": 0,
            "type": "integer"
          },
          "mtime": {
            "description": "When the entry was last modified. Absent when the filesystem recorded no time.",
            "format": "date-time",
            "type": "string"
          },
          "name": {
            "description": "The entry's name, without its directory.",
            "type": "string"
          },
          "path": {
            "description": "The entry's absolute path in the session's mounted filesystems.",
            "type": "string"
          },
          "size": {
            "description": "The size in bytes, as the filesystem records it. For a directory, the directory itself, not its contents.",
            "format": "int64",
            "type": "integer"
          },
          "type": {
            "description": "What the entry is: `file` a regular file; `dir` a directory; `symlink` a symbolic link, which is reported and never followed; `other` anything else, such as a device, socket or pipe. A directory listing also reports as `other`, with no metadata, a name it cannot read details for, such as one holding a space or a control character; stat its path to get them.",
            "enum": [
              "file",
              "dir",
              "symlink",
              "other"
            ],
            "type": "string"
          },
          "uid": {
            "description": "The id of the user that owns the entry.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "EstimateResponse": {
        "description": "How long restoring a backup would take from each copy of it.",
        "properties": {
          "fastest_tier": {
            "description": "The copy with the shortest estimate. A restore that names no `tier` reads it when it can serve the request: the instant tier serves new volumes and servers only. Absent when no copy can be restored from now.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          },
          "point_id": {
            "description": "The backup estimated.",
            "type": "string"
          },
          "tiers": {
            "description": "One estimate for each copy that could hold the backup, available or not.",
            "items": {
              "$ref": "#/components/schemas/TierEstimate"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "Evidence": {
        "description": "Another region's record of taking custody of the group.",
        "properties": {
          "cloud": {
            "description": "The cloud that took it.",
            "type": "string"
          },
          "epoch": {
            "description": "The custody epoch it took the group at.",
            "format": "int64",
            "type": "integer"
          },
          "members": {
            "description": "Each member as that region brought it up. Absent when not recorded.",
            "items": {
              "$ref": "#/components/schemas/HandoverMember"
            },
            "type": "array"
          },
          "project_id": {
            "description": "The project it runs the workload in. Absent when not recorded.",
            "type": "string"
          },
          "recorded_at": {
            "description": "When it recorded that.",
            "format": "date-time",
            "type": "string"
          },
          "run_id": {
            "description": "Its run.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ExclusionView": {
        "description": "One disk, or class of objects, that is left out of a plan or a backup, and why.",
        "properties": {
          "detail": {
            "description": "The reason in words for people. May change between releases.",
            "type": "string"
          },
          "effect": {
            "description": "What leaving it out costs a restore. `data_loss`: the data is not in the backup and a restore does not bring it back. `regenerated`: a restore rebuilds it from metadata the backup holds, so nothing is lost.",
            "enum": [
              "data_loss",
              "regenerated"
            ],
            "type": "string"
          },
          "kind": {
            "description": "What is left out. `root`, `ephemeral`, `swap` and `config_drive` are disks a server owns; `volume` is a block storage volume; `objects` is a class of objects in a bucket.",
            "enum": [
              "root",
              "ephemeral",
              "swap",
              "config_drive",
              "volume",
              "objects"
            ],
            "type": "string"
          },
          "locator": {
            "description": "Which disk of the resource: `_disk`, `_disk.ephN`, `_disk.swap` or `_disk.config` for a disk a server owns, the attachment device such as `/dev/vdb` for a volume. Absent when not known, and for a bucket.",
            "type": "string"
          },
          "name": {
            "description": "A name for the disk, for display: usually the volume's name, or the storage image name for a disk a server owns or one its owner excluded. May be absent.",
            "type": "string"
          },
          "reason": {
            "description": "Why it is left out. The codes are those of a refusal's `reason` other than `bucket_versioning_disabled`, plus `excluded_by_owner` for a disk the plan's owner chose to leave out, `regenerated_at_restore` for a disk a restore rebuilds by itself, `object_storage_class_unreadable` for objects the gateway serves only after a restore from archive, `object_too_large_to_restore` for objects too large to write back, and `object_encryption_key_unavailable` for objects encrypted with a key the service does not hold.",
            "enum": [
              "local_disk_ephemeral",
              "non_rbd_backend",
              "luks_key_unavailable",
              "image_not_found",
              "image_unreadable",
              "image_mirror_non_primary",
              "unsupported_image_features",
              "coverage_unaccountable",
              "volume_detached_unreachable",
              "instance_mid_move",
              "instance_not_readable",
              "instance_rescued",
              "no_ceph_connection",
              "local_disk_via_compute",
              "excluded_by_owner",
              "regenerated_at_restore",
              "object_gateway_unavailable",
              "bucket_not_found",
              "bucket_unreadable",
              "object_storage_class_unreadable",
              "object_too_large_to_restore",
              "object_encryption_key_unavailable"
            ],
            "type": "string"
          },
          "resource_id": {
            "description": "The resource that has the disk: the volume's id for a volume, the server's id for a disk the server owns, the bucket's name for objects.",
            "type": "string"
          },
          "resource_kind": {
            "description": "The kind of resource: `volume` for a volume, `instance` for a disk a server owns, `bucket` for objects. A volume its owner left out of a server plan is reported as `instance`.",
            "enum": [
              "volume",
              "instance",
              "bucket"
            ],
            "type": "string"
          },
          "size_bytes": {
            "description": "The disk's size in bytes. Absent when not known.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "ExportKeyReq": {
        "description": "The dedup domain whose key to export, and the passphrase to wrap it under.",
        "properties": {
          "domain": {
            "description": "Required. The dedup domain whose key to export, as GET /v1/repositories/{id}/domain-keys lists it.",
            "type": "string"
          },
          "passphrase": {
            "description": "Required. The key the domain key is wrapped under is derived from it. It is never stored or returned; send it to the importing cloud separately from the envelope.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ExportKeyView": {
        "description": "An exported domain key, wrapped under the passphrase.",
        "properties": {
          "envelope": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PortableKey"
              }
            ],
            "description": "The wrapped key, with the repository's name and the dedup domain in its header. Pass it unchanged to the other cloud's import."
          },
          "fingerprint": {
            "description": "The key's fingerprint. Compare it with the one the importing cloud reports.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "FailbackBinding": {
        "description": "The binding that brings one member's backups back home.",
        "properties": {
          "binding": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RecoveryBinding"
              }
            ],
            "description": "The binding."
          },
          "existing": {
            "description": "True when the binding was already in place.",
            "type": "boolean"
          },
          "instance_id": {
            "description": "The member's server id here.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "FailbackBindingsReq": {
        "description": "A request to bind the recovered servers' backups back into the group's project. The body may be omitted.",
        "properties": {
          "reason": {
            "description": "Why, recorded in the audit log.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "FailbackBindingsResponse": {
        "description": "The failback bindings proposed, or created.",
        "properties": {
          "bindings": {
            "description": "One binding per member.",
            "items": {
              "$ref": "#/components/schemas/FailbackBinding"
            },
            "type": "array"
          },
          "dry_run": {
            "description": "True when nothing was created.",
            "type": "boolean"
          },
          "epoch": {
            "description": "The custody epoch that cloud recovered the group at.",
            "format": "int64",
            "type": "integer"
          },
          "from_cloud": {
            "description": "The cloud whose recovered servers the bindings adopt the backups of.",
            "type": "string"
          },
          "recorded_at": {
            "description": "When it recorded the recovery.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "Filesystem": {
        "description": "One filesystem found on a backup's disks.",
        "properties": {
          "device": {
            "description": "The device the filesystem is on, such as `/dev/sda1` or `/dev/vg0/root`. The mount operation takes it.",
            "type": "string"
          },
          "mountable": {
            "description": "Whether the session can mount it. Swap, encrypted volumes and unrecognised or unsupported types cannot be mounted.",
            "type": "boolean"
          },
          "reason": {
            "description": "Why it cannot be mounted. Absent when it can.",
            "type": "string"
          },
          "type": {
            "description": "The filesystem type as detected, such as `ext4`, `xfs` or `ntfs`; `unknown` or empty when it could not be identified.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "FloatingPlan": {
        "description": "How one floating IP of the source server is replaced.",
        "properties": {
          "pool": {
            "description": "The pool here a new address is allocated from.",
            "type": "string"
          },
          "source_address": {
            "description": "The source floating IP address. It is not kept.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "FlrSessionView": {
        "description": "An open browse session over one backup.",
        "properties": {
          "created_at": {
            "description": "When the session was opened.",
            "format": "date-time",
            "type": "string"
          },
          "deadline": {
            "description": "When the session is closed whatever happens. It closes sooner when left idle, 15 minutes by default.",
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "The session's id. The session's other operations take it.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup being browsed.",
            "type": "string"
          },
          "session": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SessionDetail"
              }
            ],
            "description": "Only when the session is opened, never in the list. What the browse found on the backup's disks and what it mounted straight away."
          }
        },
        "type": "object"
      },
      "FlrSessionViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/FlrSessionView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "GroupDoc": {
        "description": "What a protection group's home publishes about it, sealed under its members' key.",
        "properties": {
          "custody": {
            "description": "The home's custody of the workload when it wrote the document.",
            "enum": [
              "active",
              "standby",
              "released"
            ],
            "type": "string"
          },
          "custody_epoch": {
            "description": "The home's custody epoch when it wrote the document.",
            "format": "int64",
            "type": "integer"
          },
          "group_uid": {
            "description": "The group's identity across regions.",
            "type": "string"
          },
          "home_group_id": {
            "description": "The group's id in its home region, to name when asking that region to release it.",
            "type": "string"
          },
          "members": {
            "description": "The group's servers, in boot order.",
            "items": {
              "$ref": "#/components/schemas/GroupDocMember"
            },
            "type": "array"
          },
          "name": {
            "description": "The group's name.",
            "type": "string"
          },
          "origin_cloud": {
            "description": "The home's cloud name.",
            "type": "string"
          },
          "origin_cloud_uid": {
            "description": "The identity of the home's deployment. Absent when not recorded.",
            "type": "string"
          },
          "origin_site": {
            "description": "The home's site. Absent when not recorded.",
            "type": "string"
          },
          "policy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupDocPolicy"
              }
            ],
            "description": "The promise the group's home policy makes."
          },
          "published_at": {
            "description": "When the home wrote it, by its own clock.",
            "format": "date-time",
            "type": "string"
          },
          "schema_version": {
            "description": "The document's format version.",
            "type": "integer"
          },
          "source_domain": {
            "description": "That project's domain. Absent when not recorded.",
            "type": "string"
          },
          "source_project": {
            "description": "The group's project in its home.",
            "type": "string"
          },
          "version": {
            "description": "The document's version.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "GroupDocDisk": {
        "description": "One disk of a group member.",
        "properties": {
          "image": {
            "description": "The image's name in the home region when the document was written. Absent when the disk is not mirrored.",
            "type": "string"
          },
          "mirror_global_id": {
            "description": "The disk's mirror identity, where the disk is mirrored. Absent otherwise.",
            "type": "string"
          },
          "pool": {
            "description": "The pool the mirrored image is in. Absent when the disk is not mirrored.",
            "type": "string"
          },
          "resource_id": {
            "description": "The disk's id in the home region.",
            "type": "string"
          },
          "role": {
            "description": "`root` for a server's own root disk, or `volume`.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "GroupDocMember": {
        "description": "One server of the group, as its home describes it.",
        "properties": {
          "boot_order": {
            "description": "Its boot tier.",
            "type": "integer"
          },
          "dedup_domain": {
            "description": "The dedup domain its newest backup is in. A recovery region needs that domain's key, and `plain` needs none. Absent until it has a backup.",
            "type": "string"
          },
          "disks": {
            "description": "Its disks, as its newest backup holds them. Absent until it has a backup.",
            "items": {
              "$ref": "#/components/schemas/GroupDocDisk"
            },
            "type": "array"
          },
          "health": {
            "allOf": [
              {
                "$ref": "#/components/schemas/HealthCheck"
              }
            ],
            "description": "The check that says it is up after a recovery."
          },
          "instance_id": {
            "description": "The server's id in the home region.",
            "type": "string"
          },
          "name": {
            "description": "Its display name. Absent when none.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "GroupDocPolicy": {
        "description": "The recovery promise of the group's home policy.",
        "properties": {
          "name": {
            "description": "The policy's name in the home region.",
            "type": "string"
          },
          "recovery_site": {
            "description": "The site promised to recover the group. Absent when not stated.",
            "type": "string"
          },
          "rpo_target_seconds": {
            "description": "The promised recovery point objective, in seconds. Absent when none.",
            "format": "int64",
            "type": "integer"
          },
          "rto_target_seconds": {
            "description": "The promised recovery time objective, in seconds. Absent when none.",
            "format": "int64",
            "type": "integer"
          },
          "rung": {
            "description": "The rung promised.",
            "enum": [
              "cold",
              "warm",
              "hot"
            ],
            "type": "string"
          },
          "test_every_seconds": {
            "description": "How often a recovery is promised to be tested, in seconds. Absent when none.",
            "format": "int64",
            "type": "integer"
          },
          "transport": {
            "description": "How the copy reaches the recovery region.",
            "enum": [
              "rbd-mirror",
              "repository"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "GroupInventoryView": {
        "description": "One protection group a repository describes, at the newest version of its document read here. What a group's home writes is its stated intent; whether this region acts on it is a registration's decision.",
        "properties": {
          "custody_epoch": {
            "description": "The custody epoch the home held when it wrote the document.",
            "format": "int64",
            "type": "integer"
          },
          "dedup_domain": {
            "description": "The dedup domain the document was read from, whose key opened it.",
            "type": "string"
          },
          "document": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupDoc"
              }
            ],
            "description": "The document itself, as the home wrote it."
          },
          "first_seen_at": {
            "description": "When this cloud first read this version.",
            "format": "date-time",
            "type": "string"
          },
          "group_uid": {
            "description": "The group's identity, as its home minted it. A registration names it in `recovery_of.group_uid`.",
            "type": "string"
          },
          "local": {
            "description": "True when this cloud wrote the document; such a group is recovered elsewhere, not here.",
            "type": "boolean"
          },
          "member_count": {
            "description": "How many servers the document names.",
            "type": "integer"
          },
          "name": {
            "description": "The group's name in that cloud.",
            "type": "string"
          },
          "object_key": {
            "description": "The object key the document was read from.",
            "type": "string"
          },
          "observed_mtime": {
            "description": "When the object was last modified, as the repository reported it.",
            "format": "date-time",
            "type": "string"
          },
          "origin_cloud": {
            "description": "The cloud that wrote the document, the group's home.",
            "type": "string"
          },
          "published_at": {
            "description": "When the home wrote this version, by its own clock.",
            "format": "date-time",
            "type": "string"
          },
          "recovery_region": {
            "description": "The site the group's home promises it recovers at. Absent when not stated.",
            "type": "string"
          },
          "registered_as": {
            "description": "The protection group here that registers the recovery of this group. Absent when none does.",
            "type": "string"
          },
          "repository_id": {
            "description": "The repository the document was read from.",
            "type": "string"
          },
          "rung": {
            "description": "The rung the group's home promises. Absent when it states none.",
            "enum": [
              "cold",
              "warm",
              "hot"
            ],
            "type": "string"
          },
          "source_project": {
            "description": "The group's project in that cloud.",
            "type": "string"
          },
          "version": {
            "description": "The document's version. A home writes a new version whenever the group changes.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "GroupMember": {
        "description": "One server in a protection group.",
        "properties": {
          "active_instance_id": {
            "description": "The id of the server that currently runs this member. The same as `instance_id` until a recovery brings the member up as a different server.",
            "type": "string"
          },
          "boot_order": {
            "description": "The server's boot tier. Members with the same number form one tier, and lower numbers come first. A recover or a test starts a tier once the tier before it passes its health checks.",
            "type": "integer"
          },
          "health": {
            "allOf": [
              {
                "$ref": "#/components/schemas/HealthCheck"
              }
            ],
            "description": "The check that says the server is up after a recover or in a test."
          },
          "held_at": {
            "description": "When the original started holding that backup. Absent when it holds none.",
            "format": "date-time",
            "type": "string"
          },
          "held_point_id": {
            "description": "At the group's home after a release or a resync, the backup the stopped original server holds exactly, which a failback writes the difference from. Absent otherwise.",
            "type": "string"
          },
          "instance_id": {
            "description": "The server's id.",
            "type": "string"
          },
          "name": {
            "description": "A display name for the server. Absent when none was given.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "GroupMirror": {
        "description": "How a `hot` protection group's volume images are mirrored, as this region reads them from its own storage.",
        "properties": {
          "group_id": {
            "description": "The protection group's id.",
            "type": "string"
          },
          "images": {
            "description": "One entry per mirrored image of the group's members. Empty when nothing is mirrored or none has been sampled yet.",
            "items": {
              "$ref": "#/components/schemas/MirrorImageState"
            },
            "type": "array"
          },
          "rpo_target_seconds": {
            "description": "The recovery point objective the lag is judged against, in seconds. Absent when none is promised.",
            "format": "int64",
            "type": "integer"
          },
          "rung": {
            "description": "The rung the group's promise names. Absent when nothing is promised.",
            "enum": [
              "cold",
              "warm",
              "hot"
            ],
            "type": "string"
          },
          "split_brain": {
            "description": "True when any image is primary in both regions, or replication reports a split brain.",
            "type": "boolean"
          },
          "transport": {
            "description": "How the group's copy reaches the other region. Absent when nothing is promised.",
            "enum": [
              "rbd-mirror",
              "repository"
            ],
            "type": "string"
          },
          "worst_lag_seconds": {
            "description": "The largest `lag_seconds` among the images.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "GroupNoteView": {
        "description": "A protection group named in the coverage report, with why nothing is promised to it.",
        "properties": {
          "detail": {
            "description": "Why the group is promised nothing. `no_policy`: it names no policy. `policy_gone`: the policy it names no longer exists. `policy_has_no_recovery`: the policy states no recovery section.",
            "enum": [
              "no_policy",
              "policy_gone",
              "policy_has_no_recovery"
            ],
            "type": "string"
          },
          "group_id": {
            "description": "The group's id.",
            "type": "string"
          },
          "name": {
            "description": "The group's name.",
            "type": "string"
          },
          "policy_id": {
            "description": "The policy the group names. Absent when it names none.",
            "type": "string"
          },
          "policy_name": {
            "description": "The policy's name. Absent when the group names none or the policy no longer exists.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "GroupReadiness": {
        "description": "What a recovery of the group would use, member by member, and what it would refuse right now. On a recovery it is measured here, from the standbys and adopted backups this region holds. On a group created here it is what the recovery region last reported, read from the records it writes into its own repositories; this region cannot measure it itself.",
        "properties": {
          "checked_at": {
            "description": "When this answer was composed.",
            "format": "date-time",
            "type": "string"
          },
          "custody_epoch": {
            "description": "The custody epoch this region holds.",
            "format": "int64",
            "type": "integer"
          },
          "delegation": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DelegationState"
              }
            ],
            "description": "Whether a member of the group's project has armed it here, and until when."
          },
          "doc_version": {
            "description": "The group's document version, as on the protection group.",
            "format": "int64",
            "type": "integer"
          },
          "group_id": {
            "description": "The group's id in this region.",
            "type": "string"
          },
          "group_uid": {
            "description": "The group's identity across regions.",
            "type": "string"
          },
          "issues": {
            "description": "What stands in the way at the level of the whole group, each with a stable code. Absent when nothing does. See the issue for the codes.",
            "items": {
              "$ref": "#/components/schemas/ReadinessIssue"
            },
            "type": "array"
          },
          "members": {
            "description": "Each member's own answer, in boot order.",
            "items": {
              "$ref": "#/components/schemas/MemberReadiness"
            },
            "type": "array"
          },
          "ready": {
            "description": "True when nothing below would refuse a recovery now. Warnings do not count.",
            "type": "boolean"
          },
          "recovery_region": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RemoteReadiness"
              }
            ],
            "description": "On a group created here, the newest readiness the recovery region reported. Absent until one has been read, and on a recovery."
          },
          "role": {
            "description": "Whether this is the group's home or its recovery; see the protection group.",
            "enum": [
              "primary",
              "recovery"
            ],
            "type": "string"
          },
          "rung": {
            "description": "The rung the group's home promises. Absent when it cannot be told.",
            "enum": [
              "cold",
              "warm",
              "hot"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "GroupStandby": {
        "description": "Where a recovery keeps each member's standby.",
        "properties": {
          "pool": {
            "description": "The pool the standbys are kept in.",
            "type": "string"
          },
          "prefix": {
            "description": "Put before each disk's id to name a standby's images. Absent when empty.",
            "type": "string"
          },
          "site_id": {
            "description": "The site the standbys are kept at.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "GroupView": {
        "description": "A protection group: servers that recover together, with their boot order, health checks and network maps, under the policy whose recovery section promises their recovery. A group is either created here, where its workload runs (`role` `primary`), or registered here as the recovery of a group another region created (`role` `recovery`). The two share one `group_uid`. A recover or a test of a recovery boots the members in boot order and checks each tier's health before the next. A recover lands them on the networks `network_map` names; a test lands them on the isolated network its test policy builds.",
        "properties": {
          "accepted_gaps": {
            "description": "The mapping gaps accepted in advance, such as `network:prod-net-b`, so a recover need not refuse them: on a recovery, by its operator; on a group created here, for a failback that rebuilds a member whose original is gone. Absent when none.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "active_run_id": {
            "description": "The disaster recovery run acting on the group now. Absent when none is: the group can then be changed, deleted or given a new run.",
            "type": "string"
          },
          "armed": {
            "description": "True when a member of the group's project has given this region a standing delegation to act in the project for recovery runs. See `armed_until`.",
            "type": "boolean"
          },
          "armed_until": {
            "description": "When that delegation expires. Absent when the group is not armed.",
            "format": "date-time",
            "type": "string"
          },
          "asserted_by": {
            "description": "Who registered a recovery, as `<user id>` or `<user id>@<project id>`. Absent on a group created here.",
            "type": "string"
          },
          "bindings": {
            "description": "The recovery bindings that adopt a recovery's members' backups into its project, those it created and those it found already in place. Present on a recovery when the token may read bindings, and on the answer to registering one.",
            "items": {
              "$ref": "#/components/schemas/RecoveryBinding"
            },
            "type": "array"
          },
          "bubble_policy_id": {
            "description": "The test policy a recovery's tests run under. Absent when none.",
            "type": "string"
          },
          "created_at": {
            "description": "When the group was created.",
            "format": "date-time",
            "type": "string"
          },
          "custody": {
            "description": "Where the workload stands as far as this region knows. `active`: it runs here. `standby`: this region holds a recoverable copy. `released`: it was stopped here and handed over to the other region. A new group starts `active`, a new recovery `standby`.",
            "enum": [
              "active",
              "standby",
              "released"
            ],
            "type": "string"
          },
          "custody_epoch": {
            "description": "A counter that goes up by one each time custody of the workload moves between regions, so either region can order what it reads without asking the other. A new group starts at 1; a recovery starts at, and follows, the epoch its home publishes.",
            "format": "int64",
            "type": "integer"
          },
          "doc_published_at": {
            "description": "When the group's newest document version was written. Absent until one is, and on a recovery.",
            "format": "date-time",
            "type": "string"
          },
          "doc_version": {
            "description": "The version of the group's document: on a group created here, the newest written to its repositories, 0 until the first is written; on a recovery, the newest it has followed.",
            "format": "int64",
            "type": "integer"
          },
          "domain_id": {
            "description": "The domain of the owning project.",
            "type": "string"
          },
          "group_uid": {
            "description": "The group's identity across regions, minted where it was created. A recovery carries the uid of the group it recovers.",
            "type": "string"
          },
          "id": {
            "description": "The group's id in this region.",
            "type": "string"
          },
          "mapping_profile_id": {
            "description": "The recovery profile a recover resolves flavors, networks, zones and volume types through: on a recovery, every recover's; on a group created here, the failback's, for a member it rebuilds because its original is gone. Absent when none.",
            "type": "string"
          },
          "members": {
            "description": "The servers in the group, each with its boot order and health check. For a recovery, the members its home's newest document names.",
            "items": {
              "$ref": "#/components/schemas/GroupMember"
            },
            "type": "array"
          },
          "name": {
            "description": "The group's name, unique within its project.",
            "type": "string"
          },
          "network_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Where each network lands at the disaster recovery site: the source network's name, as captured with the server, to the name of a network at that site. Null when none was given.",
            "type": "object"
          },
          "next_test_at": {
            "description": "When a recovery's next test is due. Absent when no cadence is set.",
            "format": "date-time",
            "type": "string"
          },
          "origin_cloud": {
            "description": "The cloud that created the group, for a recovery. Absent on a group created here.",
            "type": "string"
          },
          "policy_id": {
            "description": "The protection policy whose recovery section is the group's promise, from GET /v1/policies. For a recovery, this region's own policy, whose recovery site is the group's home. Empty on a group created before groups named a policy: such a group is promised nothing until a policy is named, and GET /v1/coverage lists it under `groups_without_policy`.",
            "type": "string"
          },
          "project_id": {
            "description": "The project that owns the group. For a recovery, the project here its members' backups are adopted into.",
            "type": "string"
          },
          "readiness": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupReadiness"
              }
            ],
            "description": "What a recovery here would use and refuse now, as GET /v1/protection-groups/{id}/readiness returns it. Present only when a list asked for `include=readiness`."
          },
          "repository_id": {
            "description": "The repository a recovery reads the group's document and backups from. Absent otherwise.",
            "type": "string"
          },
          "reprotect_plan_id": {
            "description": "The backup plan that protects the servers a recover brought up here, from GET /v1/plans/{id}. A recover creates it under the group's policy once every member is healthy, so the recovered workload is backed up from then on. Absent until a recover has created one.",
            "type": "string"
          },
          "role": {
            "description": "`primary`: the group was created here, where its workload normally runs. `recovery`: the group is this region's registration of a group created in another region.",
            "enum": [
              "primary",
              "recovery"
            ],
            "type": "string"
          },
          "site_id": {
            "description": "The site the group runs at, which is the site this service runs at.",
            "type": "string"
          },
          "sla": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SlaResponse"
              }
            ],
            "description": "The group's measured protection, as GET /v1/protection-groups/{id}/sla returns it. Present only when a list asked for `include=sla`, so \"not asked for\" and \"nothing measured\" are told apart."
          },
          "source_project": {
            "description": "The group's project in the cloud that created it, for a recovery. Absent otherwise.",
            "type": "string"
          },
          "standby": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GroupStandby"
              }
            ],
            "description": "Where a recovery keeps each member's standby, when the group's policy promises the `warm` rung. Absent otherwise."
          },
          "state": {
            "description": "Where the group stands. Sampled states: `protected`, the workload runs here and every member disk is mirrored within the policy's `rpo_target`, the policy's transport is not `rbd-mirror` so there is no mirror to sample, or nothing has been sampled yet (a new group starts here); `degraded`, under an `rbd-mirror` policy a member could not be resolved to its disks, a disk is not mirrored or its mirroring state is unknown, or a disk lags beyond the policy's `rpo_target`, or a recovery could recover some members but not all; `ready`, a recovery could recover every member now; `unprotected`, a recovery could recover no member yet (a new recovery starts here). While a run is active the group is `releasing`, `recovering`, `testing`, `resyncing` or `retiring`, after the run's operation. `released` follows a completed release. `incomplete` follows a run that halted past its point of no return, until it is resumed or the workload is released. `split_brain_risk` means both regions may be running the workload: the other region took custody at the epoch held here or above, which it records the moment its recover passes its point of no return, long before that recover ends, or rbd-mirror reports a split brain. It is cleared only by a resync or an acknowledgement.",
            "enum": [
              "protected",
              "ready",
              "degraded",
              "unprotected",
              "releasing",
              "released",
              "recovering",
              "incomplete",
              "testing",
              "resyncing",
              "retiring",
              "split_brain_risk"
            ],
            "type": "string"
          },
          "state_changed_at": {
            "description": "When the group last entered a different state. Absent until it first has.",
            "format": "date-time",
            "type": "string"
          },
          "state_reason": {
            "description": "Why the group entered its state, when a run left it there. Absent otherwise.",
            "type": "string"
          },
          "superseded_epoch": {
            "description": "The highest custody epoch another region is known to have recovered at while this region still held the workload. Absent while there is none.",
            "format": "int64",
            "type": "integer"
          },
          "test_every_seconds": {
            "description": "How often a recovery is to be tested, in seconds. Absent when not set.",
            "format": "int64",
            "type": "integer"
          },
          "updated_at": {
            "description": "When the group last changed, state changes included.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "GroupViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/GroupView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "HandoffView": {
        "description": "A one-time sign-in code for the caller's own session.",
        "properties": {
          "code": {
            "description": "The code. Hand it to the console, which redeems it once with POST /v1/auth/handoff/redeem. Until it is used it signs in as the caller, so keep it out of logs.",
            "type": "string"
          },
          "expires_at": {
            "description": "When the code stops working: one minute after it was created, or when the caller's token expires if that is sooner.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "HandoverDisk": {
        "description": "One disk of a member and the disk it became in the region that brought it up.",
        "properties": {
          "forced": {
            "description": "True when the image could only be promoted by force, so writes the other region made after that snapshot are not in it.",
            "type": "boolean"
          },
          "mirror_global_id": {
            "description": "For a hot group, the mirror global id of the image that was promoted. Absent otherwise.",
            "type": "string"
          },
          "primary_snap_id": {
            "description": "For a hot group, the snapshot id, in the other region, of the mirror snapshot the promoted image held. Absent otherwise.",
            "minimum": 0,
            "type": "integer"
          },
          "resource_id": {
            "description": "The volume it became.",
            "type": "string"
          },
          "source_resource_id": {
            "description": "The disk's id in the backup it was brought back from.",
            "type": "string"
          },
          "source_role": {
            "description": "Whether that disk was the server's `root` disk or a `volume`.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "HandoverMember": {
        "description": "One member as a region recorded bringing it up.",
        "properties": {
          "addresses": {
            "description": "The server's fixed addresses there. Absent when none were read.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "disks": {
            "description": "Which disk there each of the member's disks became. Absent when not recorded.",
            "items": {
              "$ref": "#/components/schemas/HandoverDisk"
            },
            "type": "array"
          },
          "healthy_at": {
            "description": "When it passed its health check. Absent when it has not.",
            "format": "date-time",
            "type": "string"
          },
          "instance_id": {
            "description": "The server it runs as there. Absent when none was built.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup it was brought back from. Absent when none.",
            "type": "string"
          },
          "recovery_point_at": {
            "description": "The instant that backup's data is from. Absent when not known.",
            "format": "date-time",
            "type": "string"
          },
          "source_instance_id": {
            "description": "The member's server id in the group's home.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "HealthCheck": {
        "description": "How a recover or a test decides that a member is up before starting the next boot tier.",
        "properties": {
          "expect": {
            "description": "The HTTP status an `http` check expects. Defaults to 200.",
            "type": "integer"
          },
          "path": {
            "description": "The path an `http` check requests. Defaults to `/`.",
            "type": "string"
          },
          "port": {
            "description": "The port to check, 1 to 65535. Absent when not given.",
            "type": "integer"
          },
          "retries": {
            "description": "How many attempts are made before the member counts as unhealthy. 10 when none was given.",
            "type": "integer"
          },
          "timeout": {
            "description": "How long one attempt may take, in nanoseconds: a whole number of seconds, at least one. 10 seconds when none was given.",
            "format": "int64",
            "type": "integer"
          },
          "type": {
            "description": "`tcp`: a connection to `port` succeeds. `http`: a GET of `path` on `port` answers with the status in `expect`. `none`: no check, and the member counts as up once it boots.",
            "enum": [
              "tcp",
              "http",
              "none"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "ImportKeyReq": {
        "description": "An envelope exported from another cloud, with its passphrase.",
        "properties": {
          "domain": {
            "description": "Required. The dedup domain to install the key for. It must match the envelope.",
            "type": "string"
          },
          "envelope": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PortableKey"
              }
            ],
            "description": "Required. The envelope exactly as the export returned it."
          },
          "passphrase": {
            "description": "Required. The passphrase the envelope was exported with.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "InstantTier": {
        "description": "The instant tier: an rbd tier that also keeps recent backups as Ceph images. Restores are not served from it yet.",
        "properties": {
          "repository_id": {
            "description": "The id of the rbd tier.",
            "type": "string"
          },
          "within": {
            "description": "How long each backup stays there, in nanoseconds.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "InstantTierReq": {
        "description": "An instant tier: an rbd tier that also keeps recent backups as Ceph images, which restores of new volumes and servers are cloned from where this site serves it. The repository view's `serves_instant_restore` says whether it does.",
        "properties": {
          "repository_id": {
            "description": "The id of a registered rbd tier.",
            "type": "string"
          },
          "within": {
            "description": "How long each backup is kept on the instant tier, as a duration such as `72h`. Required.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "InventoryResponse": {
        "description": "What a repository holds, as this cloud can read it, one page of backups at a time.",
        "properties": {
          "entries": {
            "description": "The backups found, on this page, newest first.",
            "items": {
              "$ref": "#/components/schemas/InventoryView"
            },
            "type": "array"
          },
          "groups": {
            "description": "With `view=groups`, the protection groups the repository describes, at the newest version of each document read here; `entries` is then empty. Absent otherwise.",
            "items": {
              "$ref": "#/components/schemas/GroupInventoryView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page of entries. Absent on the last page.",
            "type": "string"
          },
          "repository": {
            "description": "The repository's name.",
            "type": "string"
          },
          "unreadable": {
            "description": "Each dedup domain in the repository that this cloud cannot read, with why, such as one whose key has not been imported. A repository that could not be opened at all is listed under the unencrypted domain, with the error. Empty when every domain could be read.",
            "items": {
              "$ref": "#/components/schemas/CatalogueCursor"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "InventoryView": {
        "description": "One backup found in the repository, as the cloud that wrote it describes it.",
        "properties": {
          "consistency": {
            "description": "What the backup's instant guarantees. `crash`: the disks as they were at one instant. `filesystem`: the guest's filesystems were frozen for the instant. `object`: a bucket, read object by object.",
            "enum": [
              "crash",
              "filesystem",
              "object"
            ],
            "type": "string"
          },
          "coverage": {
            "description": "`full`: the backup holds the whole resource. `partial`: some of its data was left out.",
            "enum": [
              "full",
              "partial"
            ],
            "type": "string"
          },
          "dedup_domain": {
            "description": "The dedup domain the backup was written into: the set of backups whose data deduplicates against each other, tied to the writer's encryption choice. Reading it needs that domain's key.",
            "type": "string"
          },
          "encryption": {
            "description": "How the backup's data is protected, as its record states it: `aes256-gcm` when sealed with its dedup domain's key, `none` when not encrypted. Absent for backups that predate the field, which are encrypted.",
            "type": "string"
          },
          "first_seen_at": {
            "description": "When this cloud first found the backup.",
            "format": "date-time",
            "type": "string"
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "The labels the backup carries, as the cloud that wrote it set them. Absent when none.",
            "type": "object"
          },
          "observed_mtime": {
            "description": "When the object was last modified, as the repository reported it.",
            "format": "date-time",
            "type": "string"
          },
          "origin_cloud": {
            "description": "The cloud that wrote it, as that cloud names itself. A binding names it in `origin_cloud`.",
            "type": "string"
          },
          "origin_region": {
            "description": "The region of that cloud. Absent when not recorded.",
            "type": "string"
          },
          "origin_site": {
            "description": "The site in that cloud that wrote it. Absent when not recorded.",
            "type": "string"
          },
          "origin_uid": {
            "description": "The identity of the deployment that wrote it. Two entries with the same `origin_cloud` and different `origin_uid` were written by two clouds using one name. Absent for backups written before it was recorded.",
            "type": "string"
          },
          "point_created_at": {
            "description": "When the backup was committed, by the writing cloud's clock.",
            "format": "date-time",
            "type": "string"
          },
          "point_id": {
            "description": "The backup's id.",
            "type": "string"
          },
          "point_key": {
            "description": "The object key the backup's record was read from.",
            "type": "string"
          },
          "repository_id": {
            "description": "The repository it was found in.",
            "type": "string"
          },
          "resource_id": {
            "description": "The resource's id in that cloud. A binding names it in `source_resource_id`.",
            "type": "string"
          },
          "resource_kind": {
            "description": "What was backed up.",
            "enum": [
              "instance",
              "volume",
              "bucket"
            ],
            "type": "string"
          },
          "schema_version": {
            "description": "The format version of the backup's record.",
            "type": "integer"
          },
          "source_domain": {
            "description": "The domain of that project. Absent when not recorded.",
            "type": "string"
          },
          "source_group": {
            "description": "The consistency set the backup was taken in, in that cloud: backups that share it hold one instant. Not a protection group; a protection group's backups carry its uid as a label. Absent when none.",
            "type": "string"
          },
          "source_project": {
            "description": "The project the resource belonged to in that cloud.",
            "type": "string"
          },
          "target_proposal": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ProjectProposal"
              }
            ],
            "description": "A project here with the same id as `source_project`, proposed as the owner to confirm. Absent when there is none or it could not be looked up."
          }
        },
        "type": "object"
      },
      "JobEvent": {
        "description": "One line of a job's account of itself.",
        "properties": {
          "at": {
            "description": "When the line was written, or last repeated.",
            "format": "date-time",
            "type": "string"
          },
          "code": {
            "description": "A stable identifier for the kind of line, for example `attachment_ready`.",
            "type": "string"
          },
          "level": {
            "description": "How serious the line is. `info`: a step or a fact. `ok`: a step finished well. `warn`: something worth attention that did not stop the job. `err`: a failure.",
            "enum": [
              "info",
              "ok",
              "warn",
              "err"
            ],
            "type": "string"
          },
          "message": {
            "description": "The line in words.",
            "type": "string"
          },
          "operator": {
            "description": "True for a line meant for operators. A project-scoped token never receives these lines. Operators only.",
            "type": "boolean",
            "x-operator-only": true
          },
          "progress": {
            "description": "True for a progress line, which is updated in place as the work advances instead of adding a new line each time.",
            "type": "boolean"
          },
          "repeat": {
            "description": "How many more times the same line was written and folded into this one. Absent when it was written once.",
            "type": "integer"
          },
          "seq": {
            "description": "The line's position in the job's log. Increases with every new line.",
            "format": "int64",
            "type": "integer"
          },
          "source": {
            "description": "Which worker wrote the line, or `manager` for the service itself. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "target": {
            "description": "The disk or bucket the line is about, matching a progress target's `key`. Absent on a job-wide line.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "JobProgress": {
        "description": "How far a job has got. Advisory: a restarted job resumes from what is in the repository, not from these numbers.",
        "properties": {
          "bytes_done": {
            "description": "Bytes read from the source on a backup, or written to the target on a restore.",
            "format": "int64",
            "type": "integer"
          },
          "bytes_total": {
            "description": "How many bytes the job expects to cover.",
            "format": "int64",
            "type": "integer"
          },
          "chunks_done": {
            "description": "How many chunks it has handled.",
            "format": "int64",
            "type": "integer"
          },
          "chunks_total": {
            "description": "How many chunks the job expects to handle.",
            "format": "int64",
            "type": "integer"
          },
          "eta_seconds": {
            "description": "Estimated seconds left while the job is running: the bytes still to cover divided by `throughput_bps`. 0 when it is not running or nothing has been measured yet.",
            "format": "int64",
            "type": "integer"
          },
          "percent": {
            "description": "`bytes_done` plus `skipped_bytes`, as a share of `bytes_total`, from 0 to 100. An instant restore reports 100 once its clones exist, with `bytes_done` and `stored_bytes` at 0.",
            "type": "number"
          },
          "phase": {
            "description": "What the job is doing now, for example `diff`, `ceph_read`, `hash_compress`, `repo_write`, `restore`, `verify` or `commit`. `cloned` on an instant restore: each disk is a clone of the instant tier's copy, so nothing was written and nothing was read from the repository.",
            "type": "string"
          },
          "skipped_bytes": {
            "description": "Bytes the source declared empty, so nothing was transferred, counted as covered.",
            "format": "int64",
            "type": "integer"
          },
          "stored_bps": {
            "description": "`stored_bytes` per second since the job started.",
            "format": "int64",
            "type": "integer"
          },
          "stored_bytes": {
            "description": "Repository traffic, in bytes. On a backup, what was actually written, so data already in the repository costs nothing. On a restore, what was fetched.",
            "format": "int64",
            "type": "integer"
          },
          "targets": {
            "description": "The same figures for each disk or bucket the job works on.",
            "items": {
              "$ref": "#/components/schemas/TargetProgress"
            },
            "type": "array"
          },
          "throughput_bps": {
            "description": "`bytes_done` per second.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "JobView": {
        "description": "A unit of work the service carries out: a backup, a restore, a verification, an expiry and so on. Its state, attempts, progress and, on GET /v1/jobs/{id}, its events.",
        "properties": {
          "attempts": {
            "description": "How many attempts have started.",
            "type": "integer"
          },
          "created_at": {
            "description": "When the job was queued.",
            "format": "date-time",
            "type": "string"
          },
          "domain_id": {
            "description": "The identity domain of that project.",
            "type": "string"
          },
          "error": {
            "description": "The last failure in words. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "error_code": {
            "description": "The last failure as a stable code, for example `resource_gone` or `chunk_missing`. Kept while a retry is pending.",
            "type": "string"
          },
          "events": {
            "description": "The job's own account of what it did, oldest first, up to the latest 500 lines. Only on GET /v1/jobs/{id}.",
            "items": {
              "$ref": "#/components/schemas/JobEvent"
            },
            "type": "array"
          },
          "finished_at": {
            "description": "When the job reached a final state. Absent until then.",
            "format": "date-time",
            "type": "string"
          },
          "group_id": {
            "description": "The protection group the job belongs to, when it has one.",
            "type": "string"
          },
          "id": {
            "description": "The job's id.",
            "type": "string"
          },
          "kind": {
            "description": "What the job does. `backup`, `restore`, `instant_restore` (a restore cloned from the instant tier), `verify` (re-read and check a backup), `expire` (erase a backup), `attach` (attach or detach a backup), `flr` (file browsing) and `copy` (write a backup's second copy) are the kinds a project sees for its own work. `gc`, `tier`, `chunk`, `flatten`, `reconcile`, `replay`, `object_map_rebuild`, `bucket_teardown` and `relock` are the service's housekeeping. `dr_release`, `dr_recover`, `dr_test`, `dr_resync`, `dr_retire` and `dr_teardown` carry out a protection group's disaster recovery runs.",
            "enum": [
              "backup",
              "restore",
              "instant_restore",
              "verify",
              "gc",
              "tier",
              "chunk",
              "expire",
              "flatten",
              "reconcile",
              "replay",
              "dr_release",
              "dr_recover",
              "dr_test",
              "dr_resync",
              "dr_retire",
              "dr_teardown",
              "attach",
              "flr",
              "object_map_rebuild",
              "bucket_teardown",
              "copy",
              "relock"
            ],
            "type": "string"
          },
          "lease_until": {
            "description": "When the current holder's claim on the job lapses if it stops reporting. Absent when nobody holds it.",
            "format": "date-time",
            "type": "string"
          },
          "max_attempts": {
            "description": "How many attempts the job gets before it is dead-lettered.",
            "type": "integer"
          },
          "next_attempt_at": {
            "description": "When a pending job may next be started.",
            "format": "date-time",
            "type": "string"
          },
          "owner": {
            "description": "The service replica or worker currently holding the job. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "plan_id": {
            "description": "The plan the job belongs to, when it has one.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup the job produces or works on, when it has one.",
            "type": "string"
          },
          "progress": {
            "allOf": [
              {
                "$ref": "#/components/schemas/JobProgress"
              }
            ],
            "description": "How far the job has got. Advisory; always present, with zeros before work starts."
          },
          "project_id": {
            "description": "The project the work is for.",
            "type": "string"
          },
          "repo_id": {
            "description": "The repository the job works against, when it has one.",
            "type": "string"
          },
          "scheduled_for": {
            "description": "The instant a backup job was due. Absent for other kinds.",
            "format": "date-time",
            "type": "string"
          },
          "site_id": {
            "description": "The site whose workers carry the job out. Absent for work the service does itself.",
            "type": "string"
          },
          "started_at": {
            "description": "When the first attempt started. Absent until then.",
            "format": "date-time",
            "type": "string"
          },
          "state": {
            "description": "`pending`: waiting to start, or waiting for its next attempt after a failed one. `claimed`: a worker has taken it. `running`: in progress. `succeeded`: done. `dead_letter`: failed and will not be retried, and `error_code` says why. `cancelled`: stopped on request, or, with `error_code` `superseded`, a scheduled backup that was not run because its plan had already committed a newer backup of the same resource.",
            "enum": [
              "pending",
              "claimed",
              "running",
              "succeeded",
              "dead_letter",
              "cancelled"
            ],
            "type": "string"
          },
          "timings": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TimingsView"
              }
            ],
            "description": "The phase timings of the backup the job produced. Only on GET /v1/jobs/{id}, for a job tied to a backup."
          },
          "updated_at": {
            "description": "When the job last changed.",
            "format": "date-time",
            "type": "string"
          },
          "waiting": {
            "description": "Why a pending job has not started. `storage_busy`: the cloud's storage is under strain and work is held until it recovers. Absent when nothing holds it.",
            "enum": [
              "storage_busy"
            ],
            "type": "string"
          },
          "waiting_reason": {
            "description": "The hold in words. Operators see the specific condition; a project sees a general sentence.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "JobViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/JobView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "LVMInfo": {
        "description": "The LVM volumes found on a backup's disks. Their logical volumes are also listed as filesystems.",
        "properties": {
          "logical_volumes": {
            "description": "The LVM logical volumes. Absent when there are none.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "physical_volumes": {
            "description": "The LVM physical volumes. Absent when there are none.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "volume_groups": {
            "description": "The LVM volume groups. Absent when there are none.",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "Ladder": {
        "description": "A retention ladder: how long every backup is kept, and for how long the first backup of each day, week, month and year is kept beyond that. A backup is kept until the longest of the terms it earns.",
        "properties": {
          "all": {
            "description": "How long every backup is kept, in nanoseconds. Absent when 0.",
            "format": "int64",
            "type": "integer"
          },
          "anchors": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Anchors"
              }
            ],
            "description": "Where weeks, months and years start."
          },
          "daily": {
            "description": "For how many days the first backup of each day is kept. Absent when 0.",
            "type": "integer"
          },
          "monthly": {
            "description": "For how many months the first backup of each month is kept. Absent when 0.",
            "type": "integer"
          },
          "weekly": {
            "description": "For how many weeks the first backup of each week is kept. Absent when 0.",
            "type": "integer"
          },
          "yearly": {
            "description": "For how many years the first backup of each year is kept. Absent when 0.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "LadderReq": {
        "description": "A retention ladder. `all` keeps every backup for a duration. `daily`, `weekly`, `monthly` and `yearly` each keep the first backup of a period for that many periods. A backup is kept until the latest date any rule gives it. A ladder that keeps nothing is refused.",
        "properties": {
          "all": {
            "description": "How long every backup is kept, as a duration such as `168h`.",
            "type": "string"
          },
          "anchors": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AnchorsReq"
              }
            ],
            "description": "Where weeks, months and years start, for this ladder and the copy tier's. Left out on creation, the defaults apply: Sunday, the first of the month and January."
          },
          "daily": {
            "description": "How many days the first backup of each day is kept.",
            "type": "integer"
          },
          "monthly": {
            "description": "How many months the first backup of each month is kept.",
            "type": "integer"
          },
          "weekly": {
            "description": "How many weeks the first backup of each week is kept.",
            "type": "integer"
          },
          "yearly": {
            "description": "How many years the first backup of each year is kept.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "LegalHoldView": {
        "description": "A legal hold, and whether it still applies.",
        "properties": {
          "active": {
            "description": "True until the hold is lifted.",
            "type": "boolean"
          },
          "id": {
            "description": "The hold's id.",
            "type": "string"
          },
          "lifted_at": {
            "description": "When the hold was lifted. Absent while it is active.",
            "format": "date-time",
            "type": "string"
          },
          "lifted_by": {
            "description": "The id of the user who lifted the hold. Absent while it is active.",
            "type": "string"
          },
          "placed_at": {
            "description": "When the hold was placed.",
            "format": "date-time",
            "type": "string"
          },
          "placed_by": {
            "description": "The id of the user who placed the hold.",
            "type": "string"
          },
          "project_id": {
            "description": "The project the held backups belong to.",
            "type": "string"
          },
          "reason": {
            "description": "Why the hold was placed.",
            "type": "string"
          },
          "scope": {
            "description": "What the hold covers. `point`: one backup. `plan`: every backup of the plan. `project`: every backup in the project. Backups taken after the hold was placed are covered too.",
            "enum": [
              "point",
              "plan",
              "project"
            ],
            "type": "string"
          },
          "target_id": {
            "description": "The id of the backup, plan or project held.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "LegalHoldViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/LegalHoldView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "Listing": {
        "description": "One directory inside a backup and what it holds.",
        "properties": {
          "entries": {
            "description": "Its entries, sorted by name.",
            "items": {
              "$ref": "#/components/schemas/Entry"
            },
            "type": "array"
          },
          "path": {
            "description": "The directory that was listed.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "LockReq": {
        "description": "A retention contract to set. Durations are strings such as `720h`; the largest unit is the hour.",
        "properties": {
          "changeable_until": {
            "description": "Until when the contract stays correctable, as an RFC 3339 time. Defaults to three days from now; a time in the past fixes the contract at once. When changing a contract still in its grace period, omitting it keeps the current date.",
            "format": "date-time",
            "type": "string"
          },
          "granularity": {
            "description": "The step retain-until dates are rounded up to, as a duration. Defaults to `168h`; when changing a contract, omitting it keeps the current step. After the grace period it cannot change, and a different value is refused.",
            "type": "string"
          },
          "max_retention": {
            "description": "The ceiling, as a duration no shorter than `min_retention`. Omit it, or send `0s`, for no ceiling.",
            "type": "string"
          },
          "min_retention": {
            "description": "Required. The floor every object is locked for, as a positive duration such as `720h`.",
            "type": "string"
          },
          "mode": {
            "description": "Required. `compliance`: nobody can delete an object before its retain-until date. `governance`: a storage user with bypass permission can.",
            "enum": [
              "compliance",
              "governance"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "ManualView": {
        "description": "The terms for manual backups of plans under a policy, from the vault of its backup repository.",
        "properties": {
          "immutable": {
            "description": "True when manual backups are under an object lock and cannot be erased before they expire.",
            "type": "boolean"
          },
          "keep_at_least": {
            "description": "The shortest time a manual backup is kept, in nanoseconds.",
            "format": "int64",
            "type": "integer"
          },
          "keep_at_most": {
            "description": "The longest time a manual backup may be kept, in nanoseconds.",
            "format": "int64",
            "type": "integer"
          },
          "max_per_day": {
            "description": "How many manual backups each plan may take per day. Absent when there is no limit.",
            "type": "integer"
          },
          "until_deleted": {
            "description": "True when a manual backup may be kept until its owner deletes it.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "MappingEntry": {
        "description": "One element of a backup and how it maps onto this cloud.",
        "properties": {
          "action": {
            "description": "How it was mapped. `explicit_map`: by the profile. `match_by_name`: a namesake here. `nearest_spec`: the smallest flavor large enough. `verbatim`: applied unchanged from the backup. `import`: the captured public key is imported. `create`: recreated from the backup. `reuse`: an existing security group is used as it stands. `reassign_from_pool`: a new floating IP from the pool. `reallocate`: a new fixed address. `drop`: left out. `default`: left to this cloud's default. `none`: nothing to map. `deferred_rules`: rules that close a cycle between security groups are left out. `backend_unproven`: the volume type's storage could not be confirmed as writable. Absent on unresolved entries.",
            "enum": [
              "explicit_map",
              "match_by_name",
              "nearest_spec",
              "verbatim",
              "import",
              "create",
              "reuse",
              "reassign_from_pool",
              "reallocate",
              "drop",
              "default",
              "none",
              "deferred_rules",
              "backend_unproven"
            ],
            "type": "string"
          },
          "element": {
            "description": "What the entry is about: the server's flavor, a flavor extra spec, its image, its availability zone, its key pair, its metadata, user data, config drive or tags, a scheduler hint, a network, subnet, fixed IP or MAC address, a floating IP, a security group or one of its rules, or a volume's type, encryption, availability zone or multiattach setting.",
            "enum": [
              "flavor",
              "flavor_extra_spec",
              "image",
              "instance_availability_zone",
              "keypair",
              "metadata",
              "user_data",
              "config_drive",
              "tags",
              "scheduler_hint",
              "network",
              "subnet",
              "fixed_ip",
              "port_mac",
              "floating_ip",
              "security_group",
              "security_group_rule",
              "volume_type",
              "volume_encryption",
              "volume_availability_zone",
              "volume_multiattach"
            ],
            "type": "string"
          },
          "reason": {
            "description": "Why it is unresolved or differs. Absent on resolved entries.",
            "type": "string"
          },
          "source": {
            "description": "The element in the backup, such as a flavor name, network name, zone or volume id. Empty where the element is general.",
            "type": "string"
          },
          "target": {
            "description": "What it maps to here. Absent when nothing does.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "MemberReadiness": {
        "description": "What a recovery would use for one member, and what would stop it.",
        "properties": {
          "applied_point_id": {
            "description": "The backup the standby holds. Absent until it holds one.",
            "type": "string"
          },
          "binding_id": {
            "description": "The recovery binding adopting this member's backups here. Absent when none does.",
            "type": "string"
          },
          "boot_order": {
            "description": "The member's boot tier.",
            "type": "integer"
          },
          "dedup_domain": {
            "description": "The dedup domain the member's backups are in, whose key a recovery needs. For a document that does not say, the domain the group's document was read from.",
            "type": "string"
          },
          "instance_id": {
            "description": "The member's server id, in the group's home.",
            "type": "string"
          },
          "issues": {
            "description": "What stands in the way of this member. Absent when nothing does.",
            "items": {
              "$ref": "#/components/schemas/ReadinessIssue"
            },
            "type": "array"
          },
          "mirror": {
            "description": "For a `hot` group, the state of each mirrored image of this member as this region reads it. Absent otherwise.",
            "items": {
              "$ref": "#/components/schemas/MirrorImageState"
            },
            "type": "array"
          },
          "name": {
            "description": "The member's display name. Absent when none.",
            "type": "string"
          },
          "point_id": {
            "description": "The newest adopted backup a `cold` recovery would restore. Absent when there is none.",
            "type": "string"
          },
          "ready": {
            "description": "True when nothing in `issues` would refuse a recovery of this member. Warnings do not count.",
            "type": "boolean"
          },
          "recovery_point_at": {
            "description": "The instant this member would recover to: the backup's commit time, or the time of the backup a standby last applied. Absent when there is nothing to recover to.",
            "format": "date-time",
            "type": "string"
          },
          "standby_id": {
            "description": "The standby holding this member, for a `warm` group. Absent when there is none.",
            "type": "string"
          },
          "standby_state": {
            "description": "That standby's state.",
            "enum": [
              "seeding",
              "replaying",
              "current",
              "degraded"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "MemberReq": {
        "description": "One server to put in a protection group.",
        "properties": {
          "boot_order": {
            "description": "The server's boot tier. Members with the same number form one tier, and lower numbers come first. Defaults to 0.",
            "type": "integer"
          },
          "health": {
            "description": "The member's health check. Absent means `none`.",
            "properties": {
              "expect": {
                "description": "The HTTP status an `http` check expects. Defaults to 200.",
                "type": "integer"
              },
              "path": {
                "description": "The path an `http` check requests. Defaults to `/`.",
                "type": "string"
              },
              "port": {
                "description": "The port to check, 1 to 65535. Required for `tcp` and `http`.",
                "type": "integer"
              },
              "retries": {
                "description": "How many attempts before the member counts as unhealthy. Must not be negative; 0 or absent means 10.",
                "type": "integer"
              },
              "timeout": {
                "description": "How long one attempt may take, as a duration such as `10s`. At least one second, and cut to whole seconds. Defaults to 10 seconds.",
                "type": "string"
              },
              "type": {
                "description": "`tcp` checks that `port` accepts a connection. `http` checks that a GET of `path` answers with `expect`. `none` performs no check. Defaults to `none`.",
                "enum": [
                  "tcp",
                  "http",
                  "none"
                ],
                "type": "string"
              }
            },
            "type": "object"
          },
          "instance_id": {
            "description": "The server's id. Required, and each server may appear once.",
            "type": "string"
          },
          "name": {
            "description": "A display name for the server.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "MirrorDisk": {
        "description": "One mirrored image of a `hot` group's member, as a run plans to promote, demote, clone or resync it.",
        "properties": {
          "boot_index": {
            "description": "The disk's place in the boot order; 0 for the boot disk.",
            "type": "integer"
          },
          "demoted_snap_id": {
            "description": "For a planned recover or failback, the demotion snapshot the release recorded. Absent otherwise.",
            "minimum": 0,
            "type": "integer"
          },
          "expected_image": {
            "description": "The name this region's volume expects the image to have, which a failback renames it back to. Absent when not known.",
            "type": "string"
          },
          "global_id": {
            "description": "The image's mirror global id, by which it is found in this region whatever it is called.",
            "type": "string"
          },
          "image": {
            "description": "The image's name in this region when the run was planned.",
            "type": "string"
          },
          "pool": {
            "description": "The pool the image is in.",
            "type": "string"
          },
          "primary_snap_id": {
            "description": "The id, in the other region, of the snapshot that snapshot is a copy of. Absent when none was chosen.",
            "minimum": 0,
            "type": "integer"
          },
          "resource_id": {
            "description": "The disk's volume id in the group's home.",
            "type": "string"
          },
          "role": {
            "description": "Whether the disk is the server's `root` disk or a `volume`.",
            "type": "string"
          },
          "size_bytes": {
            "description": "The disk's size in bytes. Absent when not known.",
            "format": "int64",
            "type": "integer"
          },
          "snap": {
            "description": "That snapshot's name. Absent when none was chosen.",
            "type": "string"
          },
          "snap_at": {
            "description": "When that snapshot was taken. Absent when none was chosen.",
            "format": "date-time",
            "type": "string"
          },
          "snap_id": {
            "description": "The id of the mirror snapshot a test clones or a recover reaches. Absent when none was chosen.",
            "minimum": 0,
            "type": "integer"
          },
          "volume_id": {
            "description": "The volume in this region the image backs. Absent when none does yet.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "MirrorImageState": {
        "description": "One mirrored image of a `hot` group, as the region that reports it reads it from its own storage.",
        "properties": {
          "error": {
            "description": "Why the image is not `ok`, in words for people. Absent when it is.",
            "type": "string"
          },
          "global_id": {
            "description": "The image's mirror global id, which both regions share whatever the image is called.",
            "type": "string"
          },
          "image": {
            "description": "The image's name in this region.",
            "type": "string"
          },
          "lag_seconds": {
            "description": "How old the newest replicated state is, in seconds.",
            "format": "int64",
            "type": "integer"
          },
          "last_complete_at": {
            "description": "When that snapshot was taken. Absent when there is none.",
            "format": "date-time",
            "type": "string"
          },
          "last_complete_snapshot": {
            "description": "The newest complete mirror snapshot of a non-primary copy. Absent on a primary, and until one completes.",
            "type": "string"
          },
          "member_instance_id": {
            "description": "The member the image belongs to, by its server id in the group's home.",
            "type": "string"
          },
          "mirror_state": {
            "description": "The replication daemon's own state for the image here, such as `up+replaying`. Absent when not reported.",
            "type": "string"
          },
          "peer_site": {
            "description": "The other region's mirroring site name. Absent when no peer reports the image.",
            "type": "string"
          },
          "peer_state": {
            "description": "The replication daemon's state for the image in the other region. Absent when not reported.",
            "type": "string"
          },
          "peer_synced_at": {
            "description": "On a primary, the instant of the newest state the other region has completed. Absent when not reported.",
            "format": "date-time",
            "type": "string"
          },
          "pool": {
            "description": "The pool the image is in.",
            "type": "string"
          },
          "role": {
            "description": "Whether this region holds the primary copy, which takes writes, or the non-primary copy.",
            "enum": [
              "primary",
              "non-primary"
            ],
            "type": "string"
          },
          "split_brain": {
            "description": "True when both regions hold a primary copy, or replication reports a split brain.",
            "type": "boolean"
          },
          "state": {
            "description": "`ok`: replicating within the promise. `lagging`: replicating, and the newest replicated state is older than the recovery point objective. `not_mirrored`: mirroring is not enabled on the image. `image_not_found_by_global_id`: no image here carries the global id. `never_synced`: no complete mirror snapshot has arrived, so nothing could be recovered from it. `split_brain`: both regions hold a primary copy, or replication reports a split brain. `no_mirror_snapshot`: nothing is taking mirror snapshots of the primary. `snapshot_failed`: a mirror snapshot this region took failed. `promoted_outside_a_run`: the copy here is primary while the workload is not active here. `unknown`: the state could not be read.",
            "enum": [
              "ok",
              "lagging",
              "not_mirrored",
              "image_not_found_by_global_id",
              "never_synced",
              "split_brain",
              "no_mirror_snapshot",
              "snapshot_failed",
              "promoted_outside_a_run",
              "unknown"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "MissedTickView": {
        "description": "A scheduled backup that did not run, and why. A plan's list holds the misses recorded in the reporting window, 7 days unless the operator set another, newest first.",
        "properties": {
          "deadline": {
            "description": "The latest time the backup could have started, from the policy's `start_within`. Absent when there is none, for example because the policy sets no `start_within` or no longer exists.",
            "format": "date-time",
            "type": "string"
          },
          "reason": {
            "description": "Why it did not run. `start_within_elapsed`: it could not have started within the policy's start window. `not_dispatched_in_time`: it was queued but not started before the start window closed, and was cancelled. `policy_unsatisfied`: the policy could not run backups at the time. `policy_retired`: the policy is retired. `policy_gone`: the policy no longer exists.",
            "enum": [
              "start_within_elapsed",
              "not_dispatched_in_time",
              "policy_unsatisfied",
              "policy_retired",
              "policy_gone"
            ],
            "type": "string"
          },
          "recorded_at": {
            "description": "When the miss was recorded.",
            "format": "date-time",
            "type": "string"
          },
          "scheduled_for": {
            "description": "The instant the backup was scheduled for.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "MissedTickViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/MissedTickView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "Mount": {
        "description": "A filesystem mounted in a browse session.",
        "properties": {
          "device": {
            "description": "The device that is mounted.",
            "type": "string"
          },
          "mount_point": {
            "description": "Where it is mounted, as an absolute path in the session.",
            "type": "string"
          },
          "type": {
            "description": "The filesystem type. Absent on mounts made by inspection.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "MountRequest": {
        "description": "What to mount in a browse session. Name `device`, or set `inspect`.",
        "properties": {
          "device": {
            "description": "The filesystem to mount, as a device listed in the session's filesystems, such as `/dev/sda1` or `/dev/vg0/root`. It must be one marked mountable.",
            "type": "string"
          },
          "inspect": {
            "description": "`true` to find the operating system on the disks and mount its filesystems where it expects them, root first. `device` and `mount_point` are then not used.",
            "type": "boolean"
          },
          "mount_point": {
            "description": "Where to mount it, as an absolute path. `/` when left out.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "MountResult": {
        "description": "The filesystems mounted in a browse session after a mount.",
        "properties": {
          "mounts": {
            "description": "Every filesystem now mounted in the session, after this mount.",
            "items": {
              "$ref": "#/components/schemas/Mount"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "MoverView": {
        "description": "A backup worker at this site: a process that reads protected disks and writes them to a repository, or the reverse for a restore.",
        "properties": {
          "alive": {
            "description": "Whether the worker sent a heartbeat in the last 90 seconds, three heartbeat intervals. A worker that is not alive is sent no work.",
            "type": "boolean"
          },
          "capabilities": {
            "description": "What the worker can do, which decides the work it is sent. `librbd` reads and writes Ceph images; `localdisk` reaches disks on compute hosts; `attach` attaches block storage volumes to itself; `s3`, `rados` and `posix` open chunk repositories with that driver; `buckets` backs up object storage buckets; `relock` extends object locks; `libguestfs` browses the files inside a backup. Absent when it reported none.",
            "items": {
              "enum": [
                "attach",
                "buckets",
                "libguestfs",
                "librbd",
                "localdisk",
                "posix",
                "rados",
                "relock",
                "s3"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "id": {
            "description": "The worker's id, as it registered itself.",
            "type": "string"
          },
          "last_seen_at": {
            "description": "The worker's last heartbeat.",
            "format": "date-time",
            "type": "string"
          },
          "max_parallel": {
            "description": "How many jobs the worker runs at once at most.",
            "type": "integer"
          },
          "running_jobs": {
            "description": "How many jobs it was running at its last heartbeat.",
            "type": "integer"
          },
          "site_id": {
            "description": "The site the worker serves, which is always this site.",
            "type": "string"
          },
          "unreachable_repos": {
            "description": "The ids of repositories the worker reports it cannot open. It is sent no work for them. Absent when it can open every repository.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "version": {
            "description": "The worker's software version.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "MoverViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/MoverView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "OffboardView": {
        "description": "The answer to offboarding a project.",
        "properties": {
          "deletion_id": {
            "description": "The id that ties every deleted plan and backup of this offboarding together.",
            "type": "string"
          },
          "detail": {
            "description": "The same, in words for people.",
            "type": "string"
          },
          "erased_by": {
            "description": "When the last of the project's backups leaves storage, the earliest date the repositories allow. Each plan can be undeleted until its own purge date.",
            "format": "date-time",
            "type": "string"
          },
          "plans": {
            "description": "How many plans were deleted.",
            "type": "integer"
          },
          "points": {
            "description": "How many backups were deleted.",
            "type": "integer"
          },
          "project_id": {
            "description": "The project that was offboarded.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "OpenFLRSessionReq": {
        "description": "The backup to browse.",
        "properties": {
          "point_id": {
            "description": "The backup to browse. Required. Its state must be `committed`, `verified`, `expiring` or `lock_held`. A backup still being written, broken or deleted cannot be browsed.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "OrphanedPointView": {
        "description": "A live backup with no plan and no expiry, so nothing will expire it on its own.",
        "properties": {
          "committed_at": {
            "description": "When the backup committed. Absent for one that never finished committing.",
            "format": "date-time",
            "type": "string"
          },
          "locked_until": {
            "description": "The repository's object-lock date, when one still applies. Absent otherwise.",
            "format": "date-time",
            "type": "string"
          },
          "point_id": {
            "description": "The backup's id.",
            "type": "string"
          },
          "resource_id": {
            "description": "The id of the resource it backs up.",
            "type": "string"
          },
          "resource_kind": {
            "description": "The kind of resource.",
            "enum": [
              "volume",
              "instance",
              "bucket"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "PartialCoverageReq": {
        "description": "The plan owner's answer to an account of disks that cannot be protected.",
        "properties": {
          "accept": {
            "description": "`true` accepts the account. `false` declines it, and the request is refused with `protection_refused`. The decision is recorded either way.",
            "type": "boolean"
          },
          "digest": {
            "description": "The digest of the account being accepted, exactly as the `partial_coverage_unaccepted` refusal or GET /v1/resources returned it. It must match the account the resources have at the time of this request.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PatchGroupReq": {
        "description": "Changes to a protection group. Fields left out stay as they are. A recovery accepts `name`, `policy_id`, the network maps and the fields from `mapping_profile_id` on, and only from an operator. A group created here refuses `bubble_policy_id` and `test_every`, and takes `mapping_profile_id` and `accept_gaps` for its failback; changing its `mapping_profile_id` is an operator's act.",
        "properties": {
          "accept_gaps": {
            "description": "The complete new list of accepted mapping gaps: a recovery's for every recover, a group created here's for the members its failback rebuilds. `[]` clears it.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "bubble_policy_id": {
            "description": "A recovery's new test policy. An empty string clears it.",
            "type": "string"
          },
          "mapping_profile_id": {
            "description": "The group's new recovery profile: a recovery's for every recover, a group created here's for the members its failback rebuilds. An empty string clears it. Operator only.",
            "type": "string"
          },
          "members": {
            "description": "The complete new member list, replacing the old one, with the same rules as on create. An empty list is ignored. Every member must be protected by a plan under the group's policy, or under `policy_id` when the request also names one; a group that names no policy must name one in the same request. A recovery follows its home's members and refuses this field.",
            "items": {
              "$ref": "#/components/schemas/MemberReq"
            },
            "type": "array"
          },
          "name": {
            "description": "A new name, unique within the project. Must not be empty.",
            "type": "string"
          },
          "network_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "The complete new network map, replacing the old one. `{}` clears it.",
            "type": "object"
          },
          "policy_id": {
            "description": "Move the group to another protection policy. It must state a recovery section. For a group created here, every member, new or kept, must already be protected by a plan under it.",
            "type": "string"
          },
          "test_every": {
            "description": "A recovery's new test cadence, as a duration. `0s` clears it; the next test is due one cadence from now.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PatchPlanReq": {
        "description": "The body that changes a plan. Every field is optional; an absent field is left as it is.",
        "properties": {
          "consistency": {
            "description": "A new consistency for future backups. `crash` or `filesystem`; an empty string returns to the cloud's default. Not accepted for a bucket plan.",
            "enum": [
              "",
              "crash",
              "filesystem"
            ],
            "type": "string"
          },
          "enabled": {
            "description": "`false` pauses the plan: it takes no scheduled backups until set to `true` again. Only an operator may pause a plan under a mandatory policy.",
            "type": "boolean"
          },
          "excluded_disks": {
            "description": "Replaces the owner's choice of disks not to protect. An empty array protects every disk again. When `resource_ids` changes and this is absent, the previous choice is dropped.",
            "items": {
              "$ref": "#/components/schemas/DiskSelectorReq"
            },
            "type": "array"
          },
          "name": {
            "description": "A new name, unique within the project. Must not be empty.",
            "type": "string"
          },
          "partial_coverage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PartialCoverageReq"
              }
            ],
            "description": "Accepts the account of disks that cannot be protected for the new `resource_ids` or `excluded_disks`, by its digest. Refused unless a non-empty `resource_ids` or `excluded_disks` is sent with it."
          },
          "policy_id": {
            "description": "Moves the plan to another active policy offered to this project. A project-scoped token cannot move to a policy that offers less protection than the current one.",
            "type": "string"
          },
          "resource_ids": {
            "description": "Replaces the set of resources. A resource left out is retired: no more backups, and its existing backups stay restorable until they expire. An empty array retires every resource and pauses the plan; adding resources to such a plan resumes it. Under a mandatory policy only an operator may empty a running plan. The resources are inspected again.",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "PatchPolicyReq": {
        "description": "The fields of a policy to change. A field left out is kept.",
        "properties": {
          "assigned_to": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AssignmentReq"
              }
            ],
            "description": "Domains and projects to add to the assignment. Nothing is withdrawn here. To withdraw, use POST /v1/policies/{id}/assign with `remove=true`. Needs the `policy:assign` permission too."
          },
          "description": {
            "description": "A new description. An empty string clears it.",
            "type": "string"
          },
          "mandatory": {
            "description": "Turns mandatory protection on or off. Under a mandatory policy only an operator can pause or delete a plan.",
            "type": "boolean"
          },
          "name": {
            "description": "A new name, unique across the cloud. Must not be empty.",
            "type": "string"
          },
          "spec": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PatchPolicySpecReq"
              }
            ],
            "description": "The parts of the terms to change. A block left out is kept."
          },
          "visibility": {
            "description": "`public` offers the policy to every project. `assigned` offers it only to the domains and projects it is assigned to.",
            "enum": [
              "public",
              "assigned"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "PatchPolicySpecReq": {
        "description": "The parts of a policy's terms to change. A block left out is kept. A block sent replaces that whole block, so a field it leaves out is cleared. An optional block is removed with its `clear_` flag. Durations are strings such as `24h`.",
        "properties": {
          "clear_copy_tier": {
            "description": "`true` removes the copy tier. Cannot be sent with `tiers.copy`.",
            "type": "boolean"
          },
          "clear_instant_tier": {
            "description": "`true` removes the instant tier. Cannot be sent with `tiers.instant`.",
            "type": "boolean"
          },
          "clear_recovery": {
            "description": "`true` removes the recovery promise. Cannot be sent with `recovery`.",
            "type": "boolean"
          },
          "clear_verify": {
            "description": "`true` removes the verification cadence. Cannot be sent with `verify`.",
            "type": "boolean"
          },
          "keep": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LadderReq"
              }
            ],
            "description": "A new retention ladder, replacing the current one. The current anchors are kept unless `anchors` is sent. A copy tier's periods follow the anchors either way."
          },
          "recovery": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RecoveryReq"
              }
            ],
            "description": "A new recovery promise, replacing the current one."
          },
          "schedule": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ScheduleReq"
              }
            ],
            "description": "A new schedule, replacing the whole schedule block."
          },
          "tiers": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PatchTiersReq"
              }
            ],
            "description": "Changes to where backups are stored, block by block. A tier left out is kept."
          },
          "verify": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VerifyReq"
              }
            ],
            "description": "A new verification cadence, replacing the current one."
          }
        },
        "type": "object"
      },
      "PatchRepositoryReq": {
        "description": "The fields to change; absent fields stay as they are. What decides where data lives, such as the name, kind, driver, bucket, prefix, pool, namespace and chunk size, cannot change.",
        "properties": {
          "access_key": {
            "description": "A new S3 access key, sent together with `secret_key`. The pair replaces the old one. Only for the `s3` driver.",
            "type": "string"
          },
          "clear_object_lock": {
            "description": "`true` removes the retention contract. Allowed only during its grace period, and takes precedence over `object_lock`. Needs the `repository:lock` permission.",
            "type": "boolean"
          },
          "endpoint": {
            "description": "A new S3 endpoint URL. For the `s3` driver only.",
            "type": "string"
          },
          "environment_credentials": {
            "description": "For a chunk repository at another site: `true` applies new keys, a new endpoint or a new region without first listing the repository with them. Without it such a change is listed from here before it is saved. Not together with `access_key`.",
            "type": "boolean"
          },
          "object_lock": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LockReq"
              }
            ],
            "description": "A retention contract to set or change. Needs the `repository:lock` permission. During the grace period it replaces the current contract, and on an `s3` repository without object lock it adds one. After the grace period the `mode` must stay the same, `min_retention` may only rise, and `max_retention` may only rise (omit it to keep it). `changeable_until` may not be sent, and `granularity` may not change (omit it to keep it)."
          },
          "reachable_from_sites": {
            "description": "Replaces the ids of other sites that can reach this repository. An empty list clears them.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "region": {
            "description": "A new S3 region. For the `s3` driver only.",
            "type": "string"
          },
          "replica_of": {
            "description": "The id of the repository this one is a replica of. An empty string clears it.",
            "type": "string"
          },
          "secret_key": {
            "description": "A new S3 secret key, sent together with `access_key`.",
            "type": "string"
          },
          "soft_delete_grace": {
            "description": "A new grace for deleted backups and plans, as a duration. An empty string sets none. Needs the `repository:lock` permission. For chunk repositories only.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PatchTiersReq": {
        "description": "The storage to change. A block left out is kept. To remove the instant or copy tier, send `clear_instant_tier` or `clear_copy_tier` instead.",
        "properties": {
          "backup": {
            "allOf": [
              {
                "$ref": "#/components/schemas/BackupTierReq"
              }
            ],
            "description": "A new backup repository."
          },
          "copy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CopyTierReq"
              }
            ],
            "description": "A new copy tier, replacing the current one."
          },
          "instant": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InstantTierReq"
              }
            ],
            "description": "A new instant tier, replacing the current one."
          }
        },
        "type": "object"
      },
      "PlaceLegalHoldReq": {
        "description": "What to hold, and why.",
        "properties": {
          "reason": {
            "description": "Required. Why the backups are held, for whoever has to decide when to lift the hold. It is recorded on the hold and in the audit log.",
            "type": "string"
          },
          "scope": {
            "description": "Required. `point` holds one backup, `plan` every backup of a plan, `project` every backup in a project.",
            "enum": [
              "point",
              "plan",
              "project"
            ],
            "type": "string"
          },
          "target_id": {
            "description": "Required. The id of the backup or plan, both UUIDs, or of the project.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PlanCountView": {
        "description": "A count for one plan in the coverage report.",
        "properties": {
          "count": {
            "description": "How many of the plan's backups the list counts: scheduled backups missed in the window under `missed_ticks`, backups not yet copied under `points_awaiting_copy`, or backups not under an object lock for their whole life under `points_not_immutably_protected`.",
            "type": "integer"
          },
          "name": {
            "description": "The plan's name.",
            "type": "string"
          },
          "plan_id": {
            "description": "The plan's id.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PlanNoteView": {
        "description": "A plan named in the coverage report, with what is notable about it.",
        "properties": {
          "detail": {
            "description": "What is notable, as a code; see the list the plan appears in.",
            "type": "string"
          },
          "name": {
            "description": "The plan's name.",
            "type": "string"
          },
          "plan_id": {
            "description": "The plan's id.",
            "type": "string"
          },
          "policy_id": {
            "description": "The policy the plan names.",
            "type": "string"
          },
          "policy_name": {
            "description": "The policy's name. Absent when the policy no longer exists.",
            "type": "string"
          },
          "since": {
            "description": "For a paused plan, when it was paused.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PlanSLA": {
        "description": "A plan's measured protection over the last 30 days: how recent the newest backup is against the target, how reliably backups succeed, and since when backups can be restored. A value that could not be measured is absent, and the response's `unavailable` says why.",
        "properties": {
          "actual_rpo": {
            "description": "Nanoseconds since the newest committed backup. For a plan over several resources, the worst resource; a resource with no backup counts from the plan's creation. Absent when the plan has no committed backup at all.",
            "format": "int64",
            "type": "integer"
          },
          "consecutive_failures": {
            "description": "How many of the most recent concluded backup runs failed in a row.",
            "type": "integer"
          },
          "immutably_protected_since": {
            "description": "Since when a backup under an object lock exists, which no one can erase before the lock ends. Counted, and absent, like `oldest_restorable_point`.",
            "format": "date-time",
            "type": "string"
          },
          "oldest_restorable_point": {
            "description": "Since when a restorable backup exists: the oldest committed backup that is not `broken`. For a plan over several resources, the latest such date across them. Absent when there is none, when the plan's history is too long to read in full, and for a plan that covers its resources only in part.",
            "format": "date-time",
            "type": "string"
          },
          "rpo_compliance_pct": {
            "description": "The percentage, 0 to 100, of the last 30 days during which the newest backup was no older than `rpo_target`. For a plan over several resources, the lowest. Absent when there is no `rpo_target`.",
            "type": "number"
          },
          "rpo_target": {
            "description": "The recovery point objective the plan's policy promises, in nanoseconds. Absent when it promises none.",
            "format": "int64",
            "type": "integer"
          },
          "success_rate_30d": {
            "description": "The percentage, 0 to 100, of backup runs concluded in the last 30 days that succeeded. Absent when none concluded, or when more ran than one read covers.",
            "type": "number"
          }
        },
        "type": "object"
      },
      "PlanSLAResponse": {
        "description": "A plan's measured protection over the last 30 days, against what its policy promises, and what the numbers are about.",
        "properties": {
          "consistency_achieved": {
            "description": "The consistency the backups delivered: the promised one when no backup fell short, otherwise that of the most recent backup that did. Absent before the first backup.",
            "enum": [
              "crash",
              "filesystem",
              "object"
            ],
            "type": "string"
          },
          "consistency_downgrade": {
            "description": "Why that backup fell short, as recorded on it, for example because the guest agent did not answer. Absent when none did.",
            "type": "string"
          },
          "consistency_promised": {
            "description": "The consistency the plan asks for. `object` for a bucket plan.",
            "enum": [
              "crash",
              "filesystem",
              "object"
            ],
            "type": "string"
          },
          "coverage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SlaCoverage"
              }
            ],
            "description": "What the measurements are about. For a plan that covers its resources only in part, the whole-resource dates are moved here as dates about the covered disks."
          },
          "plan_id": {
            "description": "The plan's id.",
            "type": "string"
          },
          "points_downgraded": {
            "description": "How many of the plan's backups were taken at a lower consistency than promised.",
            "type": "integer"
          },
          "sla": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PlanSLA"
              }
            ],
            "description": "The measurements. One that could not be taken is absent and listed in `unavailable`."
          },
          "unavailable": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Each measurement that could not be taken, mapped to the reason. A field of `sla` listed here is absent, never 0; `rpo_target` is listed when the policy promises no recovery point objective. Absent when nothing is missing.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "PlanView": {
        "description": "A plan, with the policy it runs under and what it covers.",
        "properties": {
          "consistency": {
            "description": "How each backup is taken. `crash` snapshots the disks as they are; `filesystem` freezes the guest's filesystems around the snapshot when the guest allows it. Absent for a bucket plan, whose objects are read one at a time.",
            "enum": [
              "crash",
              "filesystem"
            ],
            "type": "string"
          },
          "coverage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageView"
              }
            ],
            "description": "What the plan claims to protect of its resources. Always present: `scope` is `full` unless some disks are left out."
          },
          "created_at": {
            "description": "When the plan was created.",
            "format": "date-time",
            "type": "string"
          },
          "dedup_domain": {
            "description": "The dedup domain the plan's data is written into: the set of backups whose data deduplicates against each other, and never against data outside it. It follows the encryption choice. `plain` is shared by every unencrypted plan. `w-` and a hash is a domain of the plan's own, under its own key. Absent when the plan uses the repository's shared encrypted domain. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "deleted_at": {
            "description": "When the plan was deleted. Present only on deleted plans, which only operators see.",
            "format": "date-time",
            "type": "string"
          },
          "deletion_id": {
            "description": "Groups a plan deleted with its backups, or by offboarding, with those backups, so that undeleting the plan brings them back. Present only on such deleted plans.",
            "type": "string"
          },
          "domain_id": {
            "description": "The Keystone domain of the owning project.",
            "type": "string"
          },
          "enabled": {
            "description": "True while the plan takes scheduled backups; false while it is paused or deleted.",
            "type": "boolean"
          },
          "encryption": {
            "description": "How the plan's data is encrypted, fixed at creation. `barbican` is a key of the plan's own in the key manager. `none` is unencrypted, shared with other unencrypted plans. `repository` is the repository's own key, shared with the other plans that use it.",
            "enum": [
              "barbican",
              "none",
              "repository"
            ],
            "type": "string"
          },
          "excluded_disks": {
            "description": "The disks the owner chose not to protect. Absent when there are none.",
            "items": {
              "$ref": "#/components/schemas/DiskSelector"
            },
            "type": "array"
          },
          "id": {
            "description": "The plan's id.",
            "type": "string"
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "The labels set at creation, copied onto every backup. Absent when there are none.",
            "type": "object"
          },
          "last_run_at": {
            "description": "When the scheduler last took up one of the plan's scheduled backups, whether or not a backup then ran or succeeded. Absent before the first one.",
            "format": "date-time",
            "type": "string"
          },
          "missed_ticks": {
            "description": "How many scheduled backups the plan missed in the reporting window, 7 days unless the operator set another. Returned by show, create, update and undelete; absent in the list and when the plan's policy no longer exists.",
            "type": "integer"
          },
          "name": {
            "description": "The plan's name, unique within the project.",
            "type": "string"
          },
          "next_run_at": {
            "description": "When the next scheduled backup is due, at whole seconds and before any spreading delay or backup window the policy applies. Absent while the plan is paused or deleted.",
            "format": "date-time",
            "type": "string"
          },
          "paused_at": {
            "description": "When the plan was paused: by an update, by losing its last resource, by deletion, or at creation with `enabled` set to `false`. Absent while it is enabled.",
            "format": "date-time",
            "type": "string"
          },
          "policy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PolicyView"
              }
            ],
            "description": "The policy the plan runs under. Absent when that policy no longer exists."
          },
          "policy_id": {
            "description": "The protection policy the plan runs under.",
            "type": "string"
          },
          "project_id": {
            "description": "The project that owns the plan.",
            "type": "string"
          },
          "purge_after": {
            "description": "When a deleted plan is removed for good and can no longer be undeleted. Present only on deleted plans.",
            "format": "date-time",
            "type": "string"
          },
          "refusals": {
            "description": "The disks or resources the plan cannot protect as usual, and why, as recorded when its resources were last inspected. Returned by show and create, and by update when the update inspected the resources again; absent in the list and when there are none.",
            "items": {
              "$ref": "#/components/schemas/RefusalView"
            },
            "type": "array"
          },
          "resource_ids": {
            "description": "The resources the plan protects: volume ids, server ids, or a bucket's name. Resources it no longer protects are under `retired_resources`.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "resource_kind": {
            "description": "What the plan protects. `volume` is a block storage volume; `instance` is a server with all its disks; `bucket` is an object storage bucket.",
            "enum": [
              "volume",
              "instance",
              "bucket"
            ],
            "type": "string"
          },
          "retired_resources": {
            "description": "Resources taken out of the plan, by an update or because they were deleted, whose backups are still kept. Returned by show only.",
            "items": {
              "$ref": "#/components/schemas/RetiredResource"
            },
            "type": "array"
          },
          "sla": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PlanSLAResponse"
              }
            ],
            "description": "The plan's measured protection, the same object GET /v1/plans/{id}/sla returns. Present only in the list, and only when it was asked for with `include=sla`."
          },
          "unsatisfied": {
            "description": "Why the plan's policy cannot run backups right now; absent when it can. The values are the policy's own (see its `unsatisfied`), or `policy_gone` when the plan's policy no longer exists.",
            "items": {
              "enum": [
                "backup_repository_gone",
                "backup_repository_not_a_chunk_tier",
                "instant_repository_gone",
                "instant_repository_not_an_rbd_tier",
                "copy_repository_gone",
                "copy_repository_not_a_chunk_tier",
                "retention_below_repository_floor",
                "retention_above_repository_ceiling",
                "recovery_site_gone",
                "recovery_site_cannot_read_any_copy",
                "repository_unavailable",
                "policy_gone"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "updated_at": {
            "description": "When the plan was last changed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PlanViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/PlanView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "PlannedCreate": {
        "description": "One object a recovery would create here.",
        "properties": {
          "detail": {
            "description": "More about the object in words, such as a server's vCPUs and RAM, a volume being bootable, or a port getting a new fixed address.",
            "type": "string"
          },
          "kind": {
            "description": "What would be created. `disk` is a server's local root disk, which has no volume behind it but still takes storage.",
            "enum": [
              "instance",
              "volume",
              "disk",
              "port",
              "security_group",
              "floating_ip",
              "keypair"
            ],
            "type": "string"
          },
          "name": {
            "description": "Its name, or the source identifier when this cloud assigns the name.",
            "type": "string"
          },
          "size_gb": {
            "description": "The storage it takes, in GiB, for volumes and disks.",
            "format": "int64",
            "type": "integer"
          },
          "target": {
            "description": "What it lands on here, such as a flavor, network, volume type or pool.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PointCreatedView": {
        "description": "What a manual backup started: one run per resource, each with the backup it will become and the job taking it. A backup is restorable only once its job succeeds.",
        "properties": {
          "job_id": {
            "description": "The first run's job. The same as `runs[0].job_id`.",
            "type": "string"
          },
          "keep_until": {
            "description": "When the backup will be let go, after any adjustment into the vault's bounds. A hundred years out when `until_deleted` is `true`.",
            "format": "date-time",
            "type": "string"
          },
          "locked_until": {
            "description": "When the repository's immutability lock on it will lapse, for a vault whose repository locks. Absent otherwise.",
            "format": "date-time",
            "type": "string"
          },
          "plan_id": {
            "description": "The plan the backup runs in. Absent for a backup taken straight into a vault.",
            "type": "string"
          },
          "point_id": {
            "description": "The first run's backup. The same as `runs[0].point_id`.",
            "type": "string"
          },
          "runs": {
            "description": "One entry per resource backed up.",
            "items": {
              "$ref": "#/components/schemas/PointRunView"
            },
            "type": "array"
          },
          "scheduled_for": {
            "description": "The instant the backup is taken at, the request time to the second.",
            "format": "date-time",
            "type": "string"
          },
          "state": {
            "description": "The first run's job state. `pending` for a new request. A repeat of the same request within the same second answers with the job already queued, in whatever state it is.",
            "enum": [
              "pending",
              "claimed",
              "running",
              "succeeded",
              "dead_letter",
              "cancelled"
            ],
            "type": "string"
          },
          "until_deleted": {
            "description": "True when the backup is kept until somebody deletes it. Absent otherwise.",
            "type": "boolean"
          },
          "vault": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VaultRefView"
              }
            ],
            "description": "The vault the backup is kept in."
          }
        },
        "type": "object"
      },
      "PointMetricsBody": {
        "description": "A backup's sizes and timings, as carried on the backup itself. Figures that depend on how the repository deduplicates data are returned to operators only.",
        "properties": {
          "compression_ratio": {
            "description": "Data size before compression divided by size stored, for the chunks this run wrote. Above 1 means compression saved space. Operators only.",
            "type": "number",
            "x-operator-only": true
          },
          "dedup_ratio": {
            "description": "Stored size of every chunk this run produced, repeats included, divided by the stored size of the distinct ones. Operators only.",
            "type": "number",
            "x-operator-only": true
          },
          "entropy_delta": {
            "description": "`mean_entropy` minus the previous backup's. A sudden rise means the data stopped compressing, which is how a volume encrypted by ransomware looks. 0 when there is no previous backup to compare with. Operators only.",
            "type": "number",
            "x-operator-only": true
          },
          "exclusive_bytes": {
            "description": "What deleting this backup alone would free, in bytes: data no other backup of the project in the same deduplication domain references. Not additive across backups; preview a retention change to see what deleting several frees. Absent until a repository sweep has computed it. GET /v1/points/{id}/metrics reports the same figure. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "logical_allocated_bytes": {
            "description": "How much of that holds data, in bytes; for a bucket, the total size of its objects.",
            "format": "int64",
            "type": "integer"
          },
          "logical_provisioned_bytes": {
            "description": "The provisioned size of everything the backup holds, in bytes. Absent for a bucket, which has no provisioned size.",
            "format": "int64",
            "type": "integer"
          },
          "mean_entropy": {
            "description": "The average entropy of the data this run wrote, in bits per byte, from 0 to 8. Encrypted or already compressed data sits near 8. Operators only.",
            "type": "number",
            "x-operator-only": true
          },
          "new_bytes": {
            "description": "Repository space taken by data this backup was the first to write, in bytes. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "referenced_bytes": {
            "description": "The repository space the backup's data occupies, in bytes, after compression and with each distinct chunk counted once. Shared with other backups, so not additive across them. 0 on a backup that is not in the repository yet.",
            "format": "int64",
            "type": "integer"
          },
          "scanned_bytes": {
            "description": "How much source data this run read and hashed, in bytes. A run after the first reads only what changed.",
            "format": "int64",
            "type": "integer"
          },
          "t_ceph_read_ms": {
            "description": "Time spent reading source data, in milliseconds, summed over parallel workers.",
            "format": "int64",
            "type": "integer"
          },
          "t_diff_ms": {
            "description": "Time spent working out what changed since the previous backup, in milliseconds, summed over parallel workers.",
            "format": "int64",
            "type": "integer"
          },
          "t_hash_compress_ms": {
            "description": "Time spent hashing, compressing and encrypting chunks, in milliseconds, summed over parallel workers.",
            "format": "int64",
            "type": "integer"
          },
          "t_repo_write_ms": {
            "description": "Time spent writing to the repository, in milliseconds, summed over parallel workers.",
            "format": "int64",
            "type": "integer"
          },
          "transferred_bytes": {
            "description": "How many bytes this run actually wrote to the repository. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "unavailable": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "For each figure left out, the reason, as a stable code. `namespace_has_no_provisioned_size`: a bucket has no provisioned size. `sweep_pending`: a repository sweep has not computed it yet. A project-scoped token is never told about operator-only figures.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "PointMetricsView": {
        "description": "A backup's sizes, ratios and phase timings, rendered for the caller's audience, with a reason for every figure it leaves out.",
        "properties": {
          "compression_ratio": {
            "description": "Data size before compression divided by size stored, for the chunks this run wrote. Above 1 means compression saved space. Operators only.",
            "type": "number",
            "x-operator-only": true
          },
          "dedup_ratio": {
            "description": "Stored size of every chunk this run produced, repeats included, divided by the stored size of the distinct ones. Operators only.",
            "type": "number",
            "x-operator-only": true
          },
          "entropy_delta": {
            "description": "`mean_entropy` minus the previous backup's. A sudden rise means the data stopped compressing. 0 when there is no previous backup. Operators only.",
            "type": "number",
            "x-operator-only": true
          },
          "exclusive_bytes": {
            "description": "What deleting this backup alone would free, in bytes: data no other backup of the project in the same deduplication domain references. Not additive across backups. The same figure the backup itself carries. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "freeze_duration_ms": {
            "description": "How long the guest's filesystems were frozen, in milliseconds. 0 when nothing was frozen.",
            "format": "int64",
            "type": "integer"
          },
          "logical_allocated_bytes": {
            "description": "How much of that holds data, in bytes; for a bucket, the total size of its objects.",
            "format": "int64",
            "type": "integer"
          },
          "logical_provisioned_bytes": {
            "description": "The provisioned size of everything the backup holds, in bytes. Absent for a bucket.",
            "format": "int64",
            "type": "integer"
          },
          "mean_entropy": {
            "description": "The average entropy of the data this run wrote, in bits per byte, from 0 to 8. Operators only.",
            "type": "number",
            "x-operator-only": true
          },
          "new_bytes": {
            "description": "Repository space taken by data this backup was the first to write, in bytes. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "point_id": {
            "description": "The backup's id.",
            "type": "string"
          },
          "referenced_bytes": {
            "description": "The repository space the backup's data occupies, in bytes, after compression and with each distinct chunk counted once. Not additive across backups. Absent, with a reason, while the backup is not in the repository.",
            "format": "int64",
            "type": "integer"
          },
          "scanned_bytes": {
            "description": "How much source data this run read and hashed, in bytes. A run after the first reads only what changed.",
            "format": "int64",
            "type": "integer"
          },
          "scope": {
            "description": "Which view this is. `tenant`: what the project itself sees. `admin`: the operator view, with the figures that depend on how the repository deduplicates data.",
            "enum": [
              "tenant",
              "admin"
            ],
            "type": "string"
          },
          "tier_state": {
            "description": "Which storage holds the data, as on the backup.",
            "enum": [
              "hot_only",
              "cold_only",
              "both"
            ],
            "type": "string"
          },
          "timings": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TimingsView"
              }
            ],
            "description": "Time spent in each phase of the backup."
          },
          "transferred_bytes": {
            "description": "How many bytes this run actually wrote to the repository. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "unavailable": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "For each figure left out, the reason, as a stable code. `namespace_has_no_provisioned_size`: a bucket has no provisioned size. `rbd_only_no_manifests`: the backup is not kept in a repository, so there is nothing to measure. `not_yet_chunked`: the backup is not in the repository yet. `sweep_pending`: a repository sweep has not computed it yet. A project-scoped token is never told about operator-only figures.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "PointObjectOwedView": {
        "description": "A committed backup whose point object the manager has not completed yet.",
        "properties": {
          "attempts": {
            "description": "How many times completing it has failed. 0 while the first attempt is pending.",
            "type": "integer"
          },
          "last_error": {
            "description": "Why the last attempt failed, in words for people. Absent before the first failure.",
            "type": "string"
          },
          "owed_since": {
            "description": "When completing its point object first became owed.",
            "format": "date-time",
            "type": "string"
          },
          "plan_id": {
            "description": "The plan that took it. Absent for a backup taken outside a plan.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup's id.",
            "type": "string"
          },
          "resource_id": {
            "description": "The id of the resource it backs up.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PointRunView": {
        "description": "One resource's run of a manual backup.",
        "properties": {
          "job_id": {
            "description": "The job taking it. Poll GET /v1/jobs/{id}.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup this run will commit.",
            "type": "string"
          },
          "resource_id": {
            "description": "The server, volume or bucket this run backs up. Absent when the plan names none.",
            "type": "string"
          },
          "state": {
            "description": "The state of the job taking the backup, with the same values as a job's `state`. The backup is restorable once the job is `succeeded`.",
            "enum": [
              "pending",
              "claimed",
              "running",
              "succeeded",
              "dead_letter",
              "cancelled"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "PointView": {
        "description": "One backup of one resource. A project-scoped token sees what was protected and how large it is; fields marked operator-only describe how the repository stores it.",
        "properties": {
          "buckets": {
            "description": "The buckets the backup holds. Absent for a server or a volume.",
            "items": {
              "$ref": "#/components/schemas/BucketRef"
            },
            "type": "array"
          },
          "chunk_repo_id": {
            "description": "The repository that holds the backup's data.",
            "type": "string"
          },
          "committed_at": {
            "description": "When the backup was written in full and became restorable. Absent until then.",
            "format": "date-time",
            "type": "string"
          },
          "consistency": {
            "description": "What the backup's instant guarantees. `crash`: the disks as they were at one instant, as after a power cut. `filesystem`: the guest's filesystems were frozen for the instant, so they are clean. `object`: a bucket, read object by object; each object is whole, the bucket as a whole is not from one instant.",
            "enum": [
              "crash",
              "filesystem",
              "object"
            ],
            "type": "string"
          },
          "copied_at": {
            "description": "When the second copy finished. Absent while it is pending or when no copy is kept.",
            "format": "date-time",
            "type": "string"
          },
          "copy_repo_id": {
            "description": "The repository a second copy is written to, when the policy keeps one. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "coverage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageView"
              }
            ],
            "description": "What the backup's exclusions add up to, with the backup's own statement of what it holds."
          },
          "created_at": {
            "description": "When the service recorded the backup.",
            "format": "date-time",
            "type": "string"
          },
          "dedup_domain": {
            "description": "The dedup domain the data was written into: the set of backups in the repository whose data deduplicates against each other. It follows the encryption choice. For example, `plain` holds unencrypted data, `default` data under the repository's own key, and `w-` with a hash the data of one plan with a key of its own. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "deleted_at": {
            "description": "When the backup was deleted. A deleted backup is hidden from the project at once and erased at `purge_after`. Operators only.",
            "format": "date-time",
            "type": "string",
            "x-operator-only": true
          },
          "deletion_id": {
            "description": "The deletion that hid this backup. Every backup hidden by one plan or project deletion shares it. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "disks": {
            "description": "The disks the backup holds, one entry each. Empty for a bucket.",
            "items": {
              "$ref": "#/components/schemas/DiskRef"
            },
            "type": "array"
          },
          "domain_id": {
            "description": "The identity domain of the owning project.",
            "type": "string"
          },
          "erases_at": {
            "description": "When the backup's data will actually leave the repository: the purge date of a deleted backup, otherwise the later of `expires_at` and the repository lock. Operators only.",
            "format": "date-time",
            "type": "string",
            "x-operator-only": true
          },
          "exclusions": {
            "description": "Disks or object classes of the resource that the backup does not contain, and why. Absent when it contains everything. The same list, with what it adds up to, is in `coverage`.",
            "items": {
              "$ref": "#/components/schemas/DiskExclusion"
            },
            "type": "array"
          },
          "expires_at": {
            "description": "When retention lets the backup go. Absent until retention has decided. A backup kept until deleted carries a date a hundred years out.",
            "format": "date-time",
            "type": "string"
          },
          "freeze_duration_ms": {
            "description": "How long the guest's filesystems were frozen, in milliseconds. 0 when nothing was frozen.",
            "format": "int64",
            "type": "integer"
          },
          "group_id": {
            "description": "The protection group this backup belongs to, when its resource was backed up at the same instant as others in a group. Absent otherwise.",
            "type": "string"
          },
          "id": {
            "description": "The backup's id.",
            "type": "string"
          },
          "keep_until": {
            "description": "For a manual backup, the date it is kept until, the same value as `expires_at`. Absent for scheduled backups.",
            "format": "date-time",
            "type": "string"
          },
          "last_verified_at": {
            "description": "When a verification last read and checked all of the backup's data. Absent until one has.",
            "format": "date-time",
            "type": "string"
          },
          "locked_until": {
            "description": "When the repository's immutability lock on the backup's data lapses. It cannot be erased before then. A project-scoped token sees this capped at `expires_at`. Operators see the repository's own date, which can be later because locks are extended in steps. Absent when the repository does not lock.",
            "format": "date-time",
            "type": "string"
          },
          "manual": {
            "description": "True for a backup taken on request rather than on schedule. Absent otherwise.",
            "type": "boolean"
          },
          "metrics": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PointMetricsBody"
              }
            ],
            "description": "Sizes and timings of this backup. GET /v1/points/{id}/metrics answers with the same figures and a reason for every one it leaves out."
          },
          "origin_cloud": {
            "description": "The cloud that wrote this backup, on a backup taken in another cloud and brought here to be recovered. Such a backup can be restored here but only deleted where it was taken. Absent for the backups this cloud took.",
            "type": "string"
          },
          "periods": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "For each role the backup holds, the first day (YYYY-MM-DD) of the period it holds it for, in the site's time zone.",
            "type": "object"
          },
          "plan_id": {
            "description": "The plan that took it. Absent for a manual backup taken straight into a vault.",
            "type": "string"
          },
          "point_key": {
            "description": "Where the backup's self-describing record is kept in the repository. A restore can be made from the repository alone using it. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "policy_id": {
            "description": "The protection policy whose retention governs it. Absent for a manual backup taken straight into a vault.",
            "type": "string"
          },
          "project_id": {
            "description": "The project that owns the backup.",
            "type": "string"
          },
          "purge_after": {
            "description": "When a deleted backup is erased. Until then an operator can undelete it. Operators only.",
            "format": "date-time",
            "type": "string",
            "x-operator-only": true
          },
          "quiesce_downgrade": {
            "description": "Why a backup that was meant to be filesystem-consistent was taken crash-consistent instead, as a stable code such as `no_guest_agent`, `guest_agent_unresponsive`, `quiesce_breaker_open` or `freeze_failed`. Absent when nothing was downgraded.",
            "type": "string"
          },
          "rbd_repo_id": {
            "description": "The instant tier that also keeps the backup as a Ceph image, when the policy has one. Absent otherwise.",
            "type": "string"
          },
          "release_run_id": {
            "description": "The disaster recovery run that took this backup: the final backup of a release, taken with the server off, or the backup a resync keeps of what diverged. It claims no calendar slot and is kept as the policy keeps every backup. Absent for every other backup.",
            "type": "string"
          },
          "resource_id": {
            "description": "The id of the server or volume, or the name of the bucket, that was backed up.",
            "type": "string"
          },
          "resource_kind": {
            "description": "What was backed up.",
            "enum": [
              "instance",
              "volume",
              "bucket"
            ],
            "type": "string"
          },
          "roles": {
            "description": "The calendar slots of the retention ladder this backup holds. Empty when it is kept only by the policy's keep-everything window. `periods` says which day, week, month or year each slot is for.",
            "items": {
              "enum": [
                "daily",
                "weekly",
                "monthly",
                "yearly"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "scheduled_for": {
            "description": "The instant this backup was due, by schedule or by the request that asked for it.",
            "format": "date-time",
            "type": "string"
          },
          "started_at": {
            "description": "When the snapshot was taken. Absent until the backup has started.",
            "format": "date-time",
            "type": "string"
          },
          "state": {
            "description": "Where the backup is in its life. `pending`: being taken, not restorable yet. `committed`: written in full and restorable. `verified`: committed, and the last verification re-read and checked all of its data. `broken`: verification found data missing or damaged; it cannot be restored until a verification passes again. `expiring`: being erased. `lock_held`: past its expiry, but the repository's immutability lock has not lapsed yet; it is erased when it does. Only operators see backups in this state. `deleted`: erased from the repository, or abandoned before it was ever committed.",
            "enum": [
              "pending",
              "committed",
              "verified",
              "broken",
              "expiring",
              "lock_held",
              "deleted"
            ],
            "type": "string"
          },
          "tier_state": {
            "description": "Which storage holds the data. `hot_only`: only the instant tier, as a Ceph image, not yet written to the repository. `cold_only`: only the repository. `both`: the repository and the instant tier.",
            "enum": [
              "hot_only",
              "cold_only",
              "both"
            ],
            "type": "string"
          },
          "until_deleted": {
            "description": "True when the backup is kept until somebody deletes it rather than until a date. Absent otherwise.",
            "type": "boolean"
          },
          "updated_at": {
            "description": "When the record last changed.",
            "format": "date-time",
            "type": "string"
          },
          "vault": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VaultRefView"
              }
            ],
            "description": "The vault the backup is kept in. Absent when no vault offers its repository."
          }
        },
        "type": "object"
      },
      "PointViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/PointView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "PolicyAssignment": {
        "description": "Who an `assigned` policy is offered to.",
        "properties": {
          "domains": {
            "description": "Keystone domain ids; every project in them may use the policy.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "projects": {
            "description": "Keystone project ids that may use the policy.",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "PolicyNoteView": {
        "description": "A policy that falls below the cloud's minimum protection.",
        "properties": {
          "detail": {
            "description": "What falls short, in words for people. May change between releases.",
            "type": "string"
          },
          "name": {
            "description": "The policy's name.",
            "type": "string"
          },
          "policy_id": {
            "description": "The policy's id.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PolicySpecReq": {
        "description": "A policy's terms as written in a creation request. Durations are strings such as `15m`, `24h` or `168h`. There is no unit for days, so write them in hours.",
        "properties": {
          "keep": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LadderReq"
              }
            ],
            "description": "How long each backup is kept. Required."
          },
          "recovery": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RecoveryReq"
              }
            ],
            "description": "Optional. A promise that the protected resources can be recovered at another site."
          },
          "schedule": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ScheduleReq"
              }
            ],
            "description": "When backups run. Required."
          },
          "tiers": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TiersReq"
              }
            ],
            "description": "Where backups are stored. `tiers.backup` is required."
          },
          "verify": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VerifyReq"
              }
            ],
            "description": "Optional. How often backups are checked. Without it, backups in a locked repository are checked weekly and others only by the cloud-wide verification."
          }
        },
        "type": "object"
      },
      "PolicySpecView": {
        "description": "The terms of a protection policy.",
        "properties": {
          "instant_within": {
            "description": "How long each backup is also kept on the instant tier, as a Ceph image, in nanoseconds. Absent when the policy has no instant tier.",
            "format": "int64",
            "type": "integer"
          },
          "keep": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Ladder"
              }
            ],
            "description": "How long backups are kept in the backup repository."
          },
          "recovery": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RecoveryView"
              }
            ],
            "description": "What the policy provisions for recovery at another site. Absent when nothing."
          },
          "schedule": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ScheduleView"
              }
            ],
            "description": "When backups run."
          },
          "tiers": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Tiers"
              }
            ],
            "description": "The repositories the policy writes to. Operators only.",
            "x-operator-only": true
          },
          "verify": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VerifyView"
              }
            ],
            "description": "How often backups are read back to verify them. Absent when they are not."
          }
        },
        "type": "object"
      },
      "PolicyView": {
        "description": "A protection policy: the schedule, retention, storage and recovery an operator offers, which plans run under.",
        "properties": {
          "assigned_to": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PolicyAssignment"
              }
            ],
            "description": "The domains and projects an `assigned` policy is offered to. Operators only.",
            "x-operator-only": true
          },
          "created_at": {
            "description": "When the policy was created.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "The operator's description. Absent when none was written.",
            "type": "string"
          },
          "id": {
            "description": "The policy's id.",
            "type": "string"
          },
          "mandatory": {
            "description": "True when only an operator may pause or delete a plan under this policy.",
            "type": "boolean"
          },
          "manual": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ManualView"
              }
            ],
            "description": "The terms for manual backups of plans under this policy. Absent when the vault does not allow manual backups."
          },
          "name": {
            "description": "The policy's name.",
            "type": "string"
          },
          "plan_count": {
            "description": "How many plans run under the policy. Returned by the policy operations only, never inside a plan. Operators only.",
            "type": "integer",
            "x-operator-only": true
          },
          "sentence": {
            "description": "What the policy promises, in one paragraph of English for people.",
            "type": "string"
          },
          "spec": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PolicySpecView"
              }
            ],
            "description": "The terms of the policy: its schedule, its retention and, where it has them, its storage tiers, recovery promise and verification cadence."
          },
          "state": {
            "description": "`active` policies take new plans. `deprecated` ones keep running their plans but take no new ones. `retired` ones run no backups.",
            "enum": [
              "active",
              "deprecated",
              "retired"
            ],
            "type": "string"
          },
          "unsatisfied": {
            "description": "Why the policy cannot run backups right now; absent when it can. `*_repository_gone`: a repository it names no longer exists. `*_not_a_chunk_tier`, `*_not_an_rbd_tier`: a repository it names is of the wrong kind. `retention_below_repository_floor`, `retention_above_repository_ceiling`: its retention is shorter or longer than the repository's object lock allows. `recovery_site_gone`: its recovery site no longer exists. `recovery_site_cannot_read_any_copy`: the recovery site cannot read any copy of the backups. `repository_unavailable`: a repository it writes to is not answering.",
            "items": {
              "enum": [
                "backup_repository_gone",
                "backup_repository_not_a_chunk_tier",
                "instant_repository_gone",
                "instant_repository_not_an_rbd_tier",
                "copy_repository_gone",
                "copy_repository_not_a_chunk_tier",
                "retention_below_repository_floor",
                "retention_above_repository_ceiling",
                "recovery_site_gone",
                "recovery_site_cannot_read_any_copy",
                "repository_unavailable"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "updated_at": {
            "description": "When the policy was last changed.",
            "format": "date-time",
            "type": "string"
          },
          "vault": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VaultRefView"
              }
            ],
            "description": "The vault of the policy's backup repository, which governs manual backups. Absent when the repository has none."
          },
          "visibility": {
            "description": "`public` policies are offered to every project; `assigned` ones only to the domains and projects in `assigned_to`.",
            "enum": [
              "public",
              "assigned"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "PolicyViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/PolicyView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "PortPlan": {
        "description": "How one network attachment of the source server is recreated.",
        "properties": {
          "drop_fixed_ip": {
            "description": "True when the source's fixed address is not kept and a new one is allocated.",
            "type": "boolean"
          },
          "network_id": {
            "description": "The network here it attaches to.",
            "type": "string"
          },
          "source_port_id": {
            "description": "The source port this replaces.",
            "type": "string"
          },
          "subnet_id": {
            "description": "The subnet here, when the profile maps one. Absent otherwise.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PortableKey": {
        "description": "A domain key wrapped under a passphrase, for moving it between clouds. The header fields are plain text; the key itself is only in ciphertext.",
        "properties": {
          "ciphertext": {
            "description": "The wrapped key, base64-encoded.",
            "format": "byte",
            "type": "string"
          },
          "domain": {
            "description": "The dedup domain. Also bound into the ciphertext.",
            "type": "string"
          },
          "exported_at": {
            "description": "When the key was exported, as an RFC 3339 time. Informational.",
            "type": "string"
          },
          "exported_by": {
            "description": "The user that exported the key. Informational.",
            "type": "string"
          },
          "iterations": {
            "description": "The derivation's iteration count. Exports use 600000; an import refuses fewer than 100000.",
            "type": "integer"
          },
          "kdf": {
            "description": "The function that derives the wrapping key from the passphrase.",
            "enum": [
              "pbkdf2-sha256"
            ],
            "type": "string"
          },
          "magic": {
            "description": "Identifies the format. Always `FRYK`.",
            "type": "string"
          },
          "nonce": {
            "description": "The AES-GCM nonce, base64-encoded.",
            "format": "byte",
            "type": "string"
          },
          "repository": {
            "description": "The name of the repository the key belongs to, in the exporting cloud. It is bound into the ciphertext, so editing it makes the envelope unusable. The importing cloud may name the same storage differently.",
            "type": "string"
          },
          "salt": {
            "description": "The derivation's salt, base64-encoded.",
            "format": "byte",
            "type": "string"
          },
          "source_cloud": {
            "description": "The name of the exporting cloud. Informational.",
            "type": "string"
          },
          "version": {
            "description": "The format version. Always 1.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "Problem": {
        "description": "An error, as an RFC 7807 problem document.",
        "properties": {
          "code": {
            "description": "The stable error code. Branch on this.",
            "type": "string"
          },
          "coverage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageProposal"
              }
            ],
            "description": "For `partial_coverage_unaccepted`, the account of what would and would not be protected, with the digest that accepts it."
          },
          "detail": {
            "description": "What went wrong in this request, for people. May change between releases.",
            "type": "string"
          },
          "fields": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "For `validation_failed`, each rejected field and why. For a disaster recovery refusal about members, each such member as `members.<instance id>` and why.",
            "type": "object"
          },
          "quota": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaRefusalView"
              }
            ],
            "description": "For `quota_exceeded`, the limit that was hit, what is in use and what the request needs."
          },
          "refusals": {
            "description": "For `protection_refused` and partial coverage, each resource or disk that cannot be protected and why.",
            "items": {
              "$ref": "#/components/schemas/RefusalView"
            },
            "type": "array"
          },
          "request_id": {
            "description": "The id of this request in the service's logs. Quote it when reporting a failure.",
            "type": "string"
          },
          "status": {
            "description": "The HTTP status, repeated in the body.",
            "type": "integer"
          },
          "title": {
            "description": "A short summary for people. May change between releases.",
            "type": "string"
          },
          "type": {
            "description": "A URN identifying the error. Its last segment is the code.",
            "type": "string"
          },
          "unsatisfied": {
            "description": "What the chosen policy cannot provide, one sentence each.",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "Profile": {
        "description": "A translation given inline to a dry run instead of naming a stored recovery profile.",
        "properties": {
          "az_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source availability zone to one here, for servers and volumes.",
            "type": "object"
          },
          "flavor_fallback_by_spec": {
            "description": "Allow the smallest flavor with at least the source's vCPUs, RAM and disk when no flavor matches.",
            "type": "boolean"
          },
          "flavor_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source flavor name to a flavor name here.",
            "type": "object"
          },
          "floating_pool": {
            "description": "The pool floating IPs are allocated from here.",
            "type": "string"
          },
          "import_keypair": {
            "description": "Allow a key pair with no match to be imported from the public key the backup captured.",
            "type": "boolean"
          },
          "keypair_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source key pair name to a key pair name here.",
            "type": "object"
          },
          "network_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source network name or id to a network name or id here.",
            "type": "object"
          },
          "recreate_security_groups": {
            "description": "Allow security groups with no match to be recreated from the backed-up rules.",
            "type": "boolean"
          },
          "security_group_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source security group name to a security group name here.",
            "type": "object"
          },
          "subnet_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source subnet name or id to a subnet id here.",
            "type": "object"
          },
          "volume_type_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source volume type to a volume type here.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "ProfileReq": {
        "description": "A recovery profile to create.",
        "properties": {
          "az_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source availability zone to one here, for servers and volumes.",
            "type": "object"
          },
          "flavor_fallback_by_spec": {
            "description": "Allow the smallest flavor with at least the source's vCPUs, RAM and disk when no flavor matches.",
            "type": "boolean"
          },
          "flavor_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source flavor name to a flavor name here.",
            "type": "object"
          },
          "floating_pool": {
            "description": "The pool floating IPs are allocated from here.",
            "type": "string"
          },
          "import_keypair": {
            "description": "Allow a key pair with no match to be imported from the public key the backup captured.",
            "type": "boolean"
          },
          "keypair_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source key pair name to a key pair name here.",
            "type": "object"
          },
          "name": {
            "description": "The name restores and dry runs will ask for. Required, and unique together with `origin_cloud`.",
            "type": "string"
          },
          "network_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source network name or id to a network name or id here.",
            "type": "object"
          },
          "origin_cloud": {
            "description": "The cloud whose backups this translates, as its backups name it. Absent means any cloud.",
            "type": "string"
          },
          "recreate_security_groups": {
            "description": "Allow security groups with no match to be recreated from the backed-up rules.",
            "type": "boolean"
          },
          "security_group_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source security group name to a security group name here.",
            "type": "object"
          },
          "subnet_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source subnet name or id to a subnet id here. Subnets can only be mapped to an id.",
            "type": "object"
          },
          "volume_type_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source volume type to a volume type here.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "ProjectCoverage": {
        "description": "One project's part of the coverage report. A list is absent when it is empty.",
        "properties": {
          "domain_id": {
            "description": "The project's Keystone domain. Absent when not known.",
            "type": "string"
          },
          "groups_without_policy": {
            "description": "The protection groups promised nothing: those that name no policy, with `detail` set to `no_policy`; those whose policy no longer exists, `policy_gone`; and those whose policy states no recovery, `policy_has_no_recovery`.",
            "items": {
              "$ref": "#/components/schemas/GroupNoteView"
            },
            "type": "array"
          },
          "missed_ticks": {
            "description": "Per plan, how many scheduled backups were missed in the window.",
            "items": {
              "$ref": "#/components/schemas/PlanCountView"
            },
            "type": "array"
          },
          "orphaned_points": {
            "description": "Live backups with no plan and no expiry, most often the residue of a plan purged before this was tracked, or one whose policy could not be read at purge time. Nothing will expire them on its own; an operator has to give each one a retention date.",
            "items": {
              "$ref": "#/components/schemas/OrphanedPointView"
            },
            "type": "array"
          },
          "plans": {
            "description": "How many plans the project has.",
            "type": "integer"
          },
          "plans_on_deprecated_policy": {
            "description": "The plans under a deprecated policy, with `detail` set to `deprecated`.",
            "items": {
              "$ref": "#/components/schemas/PlanNoteView"
            },
            "type": "array"
          },
          "plans_paused": {
            "description": "The plans that are paused, with `since` set to when.",
            "items": {
              "$ref": "#/components/schemas/PlanNoteView"
            },
            "type": "array"
          },
          "plans_unsatisfied": {
            "description": "The plans whose policy cannot run backups right now, with `detail` set to the first reason the policy gives.",
            "items": {
              "$ref": "#/components/schemas/PlanNoteView"
            },
            "type": "array"
          },
          "plans_without_policy": {
            "description": "The plans whose policy no longer exists, with `detail` set to `policy_gone`.",
            "items": {
              "$ref": "#/components/schemas/PlanNoteView"
            },
            "type": "array"
          },
          "point_objects_owed": {
            "description": "Committed backups whose point object still lacks the half the manager writes after the backup: the owning project, the coverage statement and the server, volume and network details a restore in another cloud reads. The backup's data is complete and restorable here. The manager completes each one in the background and retries with a growing delay; one that stays listed with a growing `attempts` needs an operator, and `last_error` says why.",
            "items": {
              "$ref": "#/components/schemas/PointObjectOwedView"
            },
            "type": "array"
          },
          "points_awaiting_copy": {
            "description": "Per plan, how many backups have not yet reached the copy repository.",
            "items": {
              "$ref": "#/components/schemas/PlanCountView"
            },
            "type": "array"
          },
          "points_not_immutably_protected": {
            "description": "Per plan, how many backups are not under an object lock for their whole life.",
            "items": {
              "$ref": "#/components/schemas/PlanCountView"
            },
            "type": "array"
          },
          "project_id": {
            "description": "The project this part of the report is about.",
            "type": "string"
          },
          "projects_over_quota": {
            "description": "The quota the project is over, if any.",
            "items": {
              "$ref": "#/components/schemas/ProjectQuotaNote"
            },
            "type": "array"
          },
          "quotas_below_locked_footprint": {
            "description": "The project's stored-bytes quota, when it is below what its locked backups occupy, with the date they will fit.",
            "items": {
              "$ref": "#/components/schemas/ProjectQuotaNote"
            },
            "type": "array"
          },
          "retired_under_mandatory": {
            "description": "Resources taken out of a plan under a mandatory policy whose backups are still kept.",
            "items": {
              "$ref": "#/components/schemas/RetiredView"
            },
            "type": "array"
          },
          "unprotected_resources": {
            "description": "The volumes and servers that could be protected whole and that no plan names. Listed only when the report is asked for one project. A volume attached to a server a plan protects is listed too.",
            "items": {
              "$ref": "#/components/schemas/UnprotectedResourceView"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ProjectProposal": {
        "description": "A project here that may be the backup's source project itself, as a Keystone shared across regions produces. A suggestion to confirm before binding. Nothing acts on it.",
        "properties": {
          "enabled": {
            "description": "Whether the project is enabled. A disabled project can still be the right one, but nothing can be restored into it until it is enabled.",
            "type": "boolean"
          },
          "name": {
            "description": "The project's name. Absent when not known.",
            "type": "string"
          },
          "project_id": {
            "description": "The project's id here.",
            "type": "string"
          },
          "reason": {
            "description": "Why the project is proposed, and what to check before accepting it.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ProjectQuotaNote": {
        "description": "A project's quota problem in the coverage report.",
        "properties": {
          "dimension": {
            "description": "The quota concerned. `resources`: how many resources the project protects. `source_bytes`: the size of the disks it protects. `stored_bytes`: what its backups occupy after deduplication.",
            "enum": [
              "resources",
              "source_bytes",
              "stored_bytes"
            ],
            "type": "string"
          },
          "domain_id": {
            "description": "The project's Keystone domain. Absent when not known.",
            "type": "string"
          },
          "in_use": {
            "description": "What the project uses, in the same unit. Under `quotas_below_locked_footprint`, the bytes its locked backups occupy.",
            "format": "int64",
            "type": "integer"
          },
          "limit": {
            "description": "The limit, a count for `resources` and bytes otherwise.",
            "format": "int64",
            "type": "integer"
          },
          "next_release_at": {
            "description": "The next date on which locked data is released. Absent when nothing is due.",
            "format": "date-time",
            "type": "string"
          },
          "project_id": {
            "description": "The project whose quota this is.",
            "type": "string"
          },
          "reclaimable": {
            "description": "True when the owner can get back under the limit themselves, by removing resources or by deleting backups that are not locked.",
            "type": "boolean"
          },
          "unmeetable_until": {
            "description": "Under `quotas_below_locked_footprint`, the date from which the locked backups fit within the limit.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ProjectRef": {
        "description": "A project a token can be scoped to.",
        "properties": {
          "domain_id": {
            "description": "The id of the domain the project belongs to.",
            "type": "string"
          },
          "enabled": {
            "description": "Whether the project is enabled. A token cannot be scoped to a disabled project.",
            "type": "boolean"
          },
          "id": {
            "description": "The project's id. Send it as `project_id` to scope a token to it.",
            "type": "string"
          },
          "name": {
            "description": "The project's name.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PromoteStandbyReq": {
        "description": "Options for the volumes a promote creates. All are optional.",
        "properties": {
          "availability_zone": {
            "description": "Accepted, but not applied in this release.",
            "type": "string"
          },
          "name_prefix": {
            "description": "Put before each disk's id to name its volume. Defaults to the standby's `target_prefix`.",
            "type": "string"
          },
          "volume_type": {
            "description": "The volume type to register the volumes with. Defaults to the block storage service's default.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PromoteStandbyResponse": {
        "description": "The volumes a promote created.",
        "properties": {
          "point_id": {
            "description": "The backup the volumes hold.",
            "type": "string"
          },
          "standby_id": {
            "description": "The standby that was promoted.",
            "type": "string"
          },
          "volumes": {
            "description": "The volumes, in the order the backup lists its disks, so the boot disk comes first.",
            "items": {
              "$ref": "#/components/schemas/PromotedVolume"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "PromotedVolume": {
        "description": "One volume a promote created.",
        "properties": {
          "bootable": {
            "description": "Whether the volume is marked bootable.",
            "type": "boolean"
          },
          "name": {
            "description": "The new volume's name.",
            "type": "string"
          },
          "size_gb": {
            "description": "The volume's size, in GiB.",
            "type": "integer"
          },
          "source_disk": {
            "description": "The id of the backed-up disk the volume was made from.",
            "type": "string"
          },
          "volume_id": {
            "description": "The new volume's id.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "PutQuotaReq": {
        "description": "The limits for one level. They replace every limit at that level. A limit left out or `null` is not set here and comes from the next level down. Name at least one.",
        "properties": {
          "resources": {
            "description": "The most servers, volumes and buckets a project may protect, each counted once however many plans name it. -1 is unlimited.",
            "format": "int64",
            "type": "integer"
          },
          "source_bytes": {
            "description": "The most bytes of resources a project may protect, counted at the size their storage reports, such as a volume's provisioned size. -1 is unlimited.",
            "format": "int64",
            "type": "integer"
          },
          "stored_bytes": {
            "description": "The most deduplicated bytes a project's backups may occupy. -1 is unlimited.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "Quota": {
        "description": "The limits set at one level, a project, a domain or the cloud's defaults, as written.",
        "properties": {
          "resources": {
            "description": "The protected-resources limit set here; -1 is unlimited. Absent when this level does not set it.",
            "format": "int64",
            "type": "integer"
          },
          "scope": {
            "description": "The level the limits are set at.",
            "enum": [
              "defaults",
              "domain",
              "project"
            ],
            "type": "string"
          },
          "source_bytes": {
            "description": "The protected-bytes limit set here; -1 is unlimited. Absent when this level does not set it.",
            "format": "int64",
            "type": "integer"
          },
          "stored_bytes": {
            "description": "The stored-bytes limit set here; -1 is unlimited. Absent when this level does not set it.",
            "format": "int64",
            "type": "integer"
          },
          "target_id": {
            "description": "The domain or project id. Absent for the defaults.",
            "type": "string"
          },
          "updated_at": {
            "description": "When the limits were last set. Absent for defaults that were never set.",
            "format": "date-time",
            "type": "string"
          },
          "updated_by": {
            "description": "The Keystone id of the user who last set them.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "QuotaAdmissionView": {
        "description": "Whether the project may add protection right now.",
        "properties": {
          "dimension": {
            "description": "The limit that closed admission. Absent while open.",
            "enum": [
              "source_bytes",
              "resources",
              "stored_bytes"
            ],
            "type": "string"
          },
          "open": {
            "description": "False when the project is over a limit. New resources, new plans, manual backups and moving a plan to a policy that is no weaker are then refused until it is under. Scheduled backups continue.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "QuotaDimensionView": {
        "description": "One limit and the project's use of it.",
        "properties": {
          "computed_at": {
            "description": "For stored bytes, when the figure was measured. Absent otherwise.",
            "format": "date-time",
            "type": "string"
          },
          "in_use": {
            "description": "How much is in use, in bytes or as a count of resources. Absent when it has not been measured, and `unavailable` says why.",
            "format": "int64",
            "type": "integer"
          },
          "limit": {
            "description": "The limit that applies. -1 is unlimited.",
            "format": "int64",
            "type": "integer"
          },
          "over": {
            "description": "True when `in_use` is above the limit.",
            "type": "boolean"
          },
          "source": {
            "description": "Where the limit comes from. `project`: the project's own limits. `domain`: its domain's. `defaults`: the cloud's defaults. `unlimited`: no level sets one.",
            "enum": [
              "project",
              "domain",
              "defaults",
              "unlimited"
            ],
            "type": "string"
          },
          "unavailable": {
            "description": "Why `in_use` is absent. `sweep_pending`: stored bytes have not been measured for this project yet.",
            "enum": [
              "sweep_pending"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "QuotaDomainView": {
        "description": "A domain's quota: the limits each of its projects gets when it sets none of its own, and where each of its projects that has a plan, or a backup taken outside a plan, stands.",
        "properties": {
          "domain_id": {
            "description": "The domain these figures are for.",
            "type": "string"
          },
          "projects": {
            "description": "Where each of those projects stands, as GET /v1/quotas answers for it.",
            "items": {
              "$ref": "#/components/schemas/QuotaView"
            },
            "type": "array"
          },
          "resources": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaDimensionView"
              }
            ],
            "description": "The protected-resources limit for each project of the domain, and where it comes from. Only `limit` and `source` are set."
          },
          "rows": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaRowsView"
              }
            ],
            "description": "The limits set for the domain and as the cloud's defaults, as written. Operators only.",
            "x-operator-only": true
          },
          "source_bytes": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaDimensionView"
              }
            ],
            "description": "The protected-bytes limit for each project of the domain, and where it comes from. Only `limit` and `source` are set, because use is counted per project."
          },
          "stored_bytes": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaDimensionView"
              }
            ],
            "description": "The stored-bytes limit for each project of the domain, and where it comes from. Only `limit` and `source` are set."
          }
        },
        "type": "object"
      },
      "QuotaPointsView": {
        "description": "A project's backups, counted by what they do to the space the project uses. Deleted backups, and backups past expiry that only a lock keeps, are not in `total`.",
        "properties": {
          "deletable": {
            "description": "Of `total`, how many the project may delete now: those not under a legal hold and not kept by a mandatory policy. A locked backup counts. Deleting it hides it at once, and its space is freed when the lock ends.",
            "type": "integer"
          },
          "held": {
            "description": "Of `total`, how many are under a legal hold.",
            "type": "integer"
          },
          "lock_held": {
            "description": "Backups past their expiry date that an object lock still keeps. Operators only.",
            "type": "integer",
            "x-operator-only": true
          },
          "locked": {
            "description": "Of `total`, how many are under an object lock now.",
            "type": "integer"
          },
          "locked_from": {
            "description": "The earliest date a lock on one of them ends, or it expires if that is sooner. Absent when none is locked.",
            "format": "date-time",
            "type": "string"
          },
          "locked_until": {
            "description": "The latest date a lock on one of them ends, or it expires if that is sooner. Absent when none is locked.",
            "format": "date-time",
            "type": "string"
          },
          "mandatory": {
            "description": "Of `total`, how many are kept by a mandatory policy.",
            "type": "integer"
          },
          "next_release_at": {
            "description": "The next day a lock ends and frees space by itself. Absent when none is due.",
            "format": "date-time",
            "type": "string"
          },
          "project_held": {
            "description": "True when a legal hold covers the whole project.",
            "type": "boolean"
          },
          "releasing": {
            "description": "Deleted backups not yet erased. They are hidden, and their space is held until they are erased. Operators only.",
            "type": "integer",
            "x-operator-only": true
          },
          "releasing_at": {
            "description": "When the first of those is erased. Operators only.",
            "format": "date-time",
            "type": "string",
            "x-operator-only": true
          },
          "total": {
            "description": "How many backups the project has.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "QuotaRefusalView": {
        "description": "The figures of a `quota_exceeded` refusal.",
        "properties": {
          "computed_at": {
            "description": "When stored bytes were last measured. Absent before the first measurement.",
            "format": "date-time",
            "type": "string"
          },
          "deletable_points": {
            "description": "How many of the project's backups its owner may delete: not under a legal hold and not under a mandatory policy. Deleting a locked one frees space only when its lock ends.",
            "type": "integer"
          },
          "dimension": {
            "description": "The limit that was hit. `resources`: how many resources the project protects. `source_bytes`: the size of the disks it protects. `stored_bytes`: what its backups occupy after deduplication.",
            "enum": [
              "resources",
              "source_bytes",
              "stored_bytes"
            ],
            "type": "string"
          },
          "in_use": {
            "description": "What the project uses now, in the same unit.",
            "format": "int64",
            "type": "integer"
          },
          "limit": {
            "description": "The limit, a count for `resources` and bytes otherwise.",
            "format": "int64",
            "type": "integer"
          },
          "locked_points": {
            "description": "How many of the project's backups are under an object lock that has not ended.",
            "type": "integer"
          },
          "locked_until": {
            "description": "When the last of those locks ends. Absent when none is locked.",
            "format": "date-time",
            "type": "string"
          },
          "next_release_at": {
            "description": "The next date on which locked data is released. Absent when nothing is due.",
            "format": "date-time",
            "type": "string"
          },
          "requested": {
            "description": "What it would use if the request were allowed. Absent when the request adds nothing and the project is already over.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "QuotaRowsView": {
        "description": "The limits behind a project's quota, one entry per level that sets any.",
        "properties": {
          "defaults": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Quota"
              }
            ],
            "description": "The cloud's default limits. Absent when none are set."
          },
          "domain": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Quota"
              }
            ],
            "description": "Its domain's limits. Absent when the domain has none or is not known."
          },
          "project": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Quota"
              }
            ],
            "description": "The project's own limits. Absent when it has none."
          }
        },
        "type": "object"
      },
      "QuotaView": {
        "description": "Where a project stands against its quota: each limit and its use, whether new protection is admitted, and what its backups hold. Operators are also shown locked bytes, the release schedule and the levels each limit comes from.",
        "properties": {
          "admission": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaAdmissionView"
              }
            ],
            "description": "Whether the project may add protection right now."
          },
          "domain_id": {
            "description": "The project's domain, learned from the token or from the project's plans and backups. Absent when unknown.",
            "type": "string"
          },
          "locked_bytes": {
            "description": "Of the stored bytes, how many are under an object lock and cannot be freed until it ends. Absent until stored bytes are measured. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "points": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaPointsView"
              }
            ],
            "description": "The project's backups, counted by what they do to the stored bytes."
          },
          "project_id": {
            "description": "The project these figures are for.",
            "type": "string"
          },
          "reclaimable_bytes": {
            "description": "The stored bytes not under a lock, the most that deleting backups could free. Absent until stored bytes are measured. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "release_schedule": {
            "description": "How many locked bytes are released each month. Absent when nothing is locked. Operators only.",
            "items": {
              "$ref": "#/components/schemas/ReleaseBucket"
            },
            "type": "array",
            "x-operator-only": true
          },
          "resources": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaDimensionView"
              }
            ],
            "description": "How many resources the project protects, against its limit."
          },
          "rows": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaRowsView"
              }
            ],
            "description": "The limits set at each level, as written. Operators only.",
            "x-operator-only": true
          },
          "sentence": {
            "description": "The project's standing in a few sentences of English for people. The wording may change between releases.",
            "type": "string"
          },
          "source_bytes": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaDimensionView"
              }
            ],
            "description": "The size of what the project protects, against its limit."
          },
          "stored_bytes": {
            "allOf": [
              {
                "$ref": "#/components/schemas/QuotaDimensionView"
              }
            ],
            "description": "The deduplicated bytes the project's backups occupy, against its limit. Measured periodically, so it can lag."
          },
          "unmeetable_until": {
            "description": "Set when the locked bytes alone are over the stored-bytes limit: the day enough locks will have ended for the project to get under it. Before then no deletion can bring it under. Operators only.",
            "format": "date-time",
            "type": "string",
            "x-operator-only": true
          }
        },
        "type": "object"
      },
      "ReadinessIssue": {
        "description": "One thing that would refuse a recovery, or that should be looked at before it is needed.",
        "properties": {
          "code": {
            "description": "A stable code. `group_has_no_policy`, `policy_has_no_recovery`: nothing is promised. `group_document_not_published`: the home has not written the group's document yet. `group_document_not_found`: a recovery's repository no longer shows the document. `recovery_delegation_missing`, `recovery_delegation_expiring`: the group is not armed, its delegation has expired, or it soon will. `binding_missing`, `binding_conflict`: the member's backups are not adopted into this group's project. `member_has_no_recoverable_state`: no backup of the member has been adopted. `member_never_synced`: no standby holds the member yet, or a mirrored image holds no complete mirror snapshot. `member_not_mirrored`: a `hot` member's disk is not a mirrored volume here. `image_not_found_by_global_id`: no image here carries a disk's mirror global id. `mirror_split_brain`: a mirrored image is live in both regions, or replication reports a split brain. `mirror_lagging`: a mirrored image is replicating, older than the promised `rpo_target`. `standby_has_no_applied_snapshot`: the standby holds a backup but nothing safe to clone from yet. `standby_degraded`: the standby's last update failed; it still holds its last backup. `recovery_point_too_old`: the newest recoverable state is older than the promised `rpo_target`. `domain_key_missing`: the member's backups are in a dedup domain this cloud holds no key for, named in the detail, or, for a document that does not say where each member is kept, the repository holds such domains. `key_manager_missing`: the member's backups are in an encrypted dedup domain and this cloud runs without a key manager, so it can never hold the key; on the group, the members this stops. `mirror_not_measured`: a `hot` member's mirrored image has not been sampled here yet, or its state could not be read. `recovery_region_not_heard`, `recovery_region_readiness_stale`, `recovery_region_epoch_mismatch`, `recovery_region_not_ready`: on a group created here, no readiness has been read from the recovery region, the newest is older than this service accepts, it reports another custody epoch, or it reports a member it could not recover. `mapping_unresolved`: resolving the member's recoverable point against the target project, as a recover's preflight does, leaves an element the registration's `accept_gaps` does not accept; the detail names each one. `mapping_unverified`: the mapping could not be evaluated here, a warning. `repository_credentials_missing`: a repository at another site, the one a recovery reads or one at the policy's recovery site, is registered here without access keys, so nothing has shown it can be read from here, a warning. `repository_unreadable`: the last read of such a repository failed, with the error, a warning.",
            "type": "string"
          },
          "detail": {
            "description": "What exactly is wrong and what to do about it.",
            "type": "string"
          },
          "warning": {
            "description": "True when this does not refuse a recovery by itself. Absent when it does.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "RecoverReq": {
        "description": "A request to bring the workload up here.",
        "properties": {
          "confirm_primary_may_be_serving_writes": {
            "description": "For an unplanned recover, the acknowledgement that the other region may still be running the workload and serving writes. Must be true, or the recover is refused with `unplanned_failover_unconfirmed`; recorded in the audit log with who gave it.",
            "type": "boolean"
          },
          "max_point_age": {
            "description": "For an unplanned recover, the oldest recovery point the caller accepts, as a positive duration such as `2h`. A member whose recovery point is older, or unknown, refuses the recover with `recovery_point_too_old`. Absent accepts any age.",
            "type": "string"
          },
          "mode": {
            "description": "Required. `planned`: act only on a release the other region completed and recorded. `unplanned`: act without one, while the other region may still be running the workload; operator only. Any other value is refused, so a typo cannot skip the confirmation an unplanned recover needs.",
            "enum": [
              "planned",
              "unplanned"
            ],
            "type": "string"
          },
          "overwrite_diverged": {
            "description": "For a planned failback at the group's home of a `cold` or `warm` group, come home even over originals that diverged from the point they hold, which a failback otherwise refuses with `return_target_diverged`. Before its point of no return the run stops and locks each such original if it is running and takes a divergence point of it under the plan that protects it, labelled as diverged from the release, so what it wrote is kept as an ordinary backup; it then writes every byte of the release's backup over it. It needs the operator's `dr:resync` permission, because like a resync it discards what diverged once it is kept. Refused anywhere else.",
            "type": "boolean"
          },
          "reason": {
            "description": "Why, recorded with the run and in the audit log.",
            "type": "string"
          },
          "recovery_point": {
            "description": "For an unplanned recover of a `cold` group, which backup each member is restored from. `aligned`, the default: each member's newest backup taken no later than shortly after the oldest of the members' newest backups, within 15 minutes or half the policy's `rpo_target`, whichever is shorter, so the members recover to about the same instant. `newest`: each member's own newest backup, fresher but not consistent across members. A `warm` group always recovers to the backup each standby holds.",
            "enum": [
              "aligned",
              "newest"
            ],
            "type": "string"
          },
          "wait_for_release": {
            "description": "For a planned recover, how long to wait for the release to be seen, as a duration such as `15m`; at most 30 minutes. Refused on an unplanned recover.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RecoveryBinding": {
        "description": "The statement that a resource another cloud backed up into a shared repository belongs to a project here. While it exists, every new backup of that resource found in the repository is adopted into the project.",
        "properties": {
          "asserted_by": {
            "description": "Who created the binding, as `<user id>`, or `<user id>@<project id>` for a project-scoped token.",
            "type": "string"
          },
          "created_at": {
            "description": "When the binding was created.",
            "format": "date-time",
            "type": "string"
          },
          "group_id": {
            "description": "The protection group whose registration created the binding. Deleting that group deletes the binding. Absent for a binding created on its own.",
            "type": "string"
          },
          "id": {
            "description": "The binding's id.",
            "type": "string"
          },
          "last_adopted_at": {
            "description": "When the last backup was adopted. Absent until the first.",
            "format": "date-time",
            "type": "string"
          },
          "last_error": {
            "description": "Why the binding last failed to adopt a backup, for example because a newer release wrote it or another project already holds its id. Cleared by the next adoption.",
            "type": "string"
          },
          "origin_cloud": {
            "description": "The cloud that wrote the backups, as they name it.",
            "type": "string"
          },
          "points_adopted": {
            "description": "How many backups the binding has adopted.",
            "format": "int64",
            "type": "integer"
          },
          "repository_id": {
            "description": "The repository the backups are read from.",
            "type": "string"
          },
          "source_project": {
            "description": "The resource's project in that cloud, for the record. Absent when not given.",
            "type": "string"
          },
          "source_resource_id": {
            "description": "The resource's id in that cloud.",
            "type": "string"
          },
          "standby_id": {
            "description": "The standby the binding registered, once the first backup was adopted. Absent until then, and for a binding without a standby pool.",
            "type": "string"
          },
          "standby_pool": {
            "description": "The pool a standby is kept in. Absent when the binding keeps no standby.",
            "type": "string"
          },
          "standby_prefix": {
            "description": "Put before each disk's id to name the standby's images. Absent when empty.",
            "type": "string"
          },
          "standby_site_id": {
            "description": "The site the binding's standby is kept at. Absent means this site.",
            "type": "string"
          },
          "target_domain_id": {
            "description": "The target project's domain, recorded on adopted backups.",
            "type": "string"
          },
          "target_project_id": {
            "description": "The project here the backups are adopted into.",
            "type": "string"
          },
          "updated_at": {
            "description": "When the binding last changed, adoption counters included.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RecoveryBindingPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/RecoveryBinding"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "RecoveryOfReq": {
        "description": "The protection group another region created, as the repository's group listing names it.",
        "properties": {
          "group_uid": {
            "description": "The group's uid. Required.",
            "type": "string"
          },
          "origin_cloud": {
            "description": "The cloud that created the group. Optional; when given it must match the document.",
            "type": "string"
          },
          "repository_id": {
            "description": "The repository the group's document was read from. Required.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RecoveryProfile": {
        "description": "A named translation from another cloud's flavors, networks, subnets, volume types, availability zones, security groups and key pairs to this cloud's. Restores and dry runs ask for one by name.",
        "properties": {
          "az_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source availability zone to one here, for servers and volumes. A zone without an entry is left unresolved, never guessed. Absent when empty.",
            "type": "object"
          },
          "created_at": {
            "description": "When the profile was created. Profiles cannot be changed.",
            "format": "date-time",
            "type": "string"
          },
          "flavor_fallback_by_spec": {
            "description": "When a flavor has no entry and no namesake here, use the smallest flavor with at least the source's vCPUs, RAM and disk.",
            "type": "boolean"
          },
          "flavor_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source flavor name to a flavor name here. Absent when empty.",
            "type": "object"
          },
          "floating_pool": {
            "description": "The pool floating IPs are allocated from here. Absent when not set.",
            "type": "string"
          },
          "id": {
            "description": "The profile's id.",
            "type": "string"
          },
          "import_keypair": {
            "description": "When a key pair has no entry and no namesake here, import the public key the backup captured.",
            "type": "boolean"
          },
          "keypair_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source key pair name to a key pair name here. Absent when empty.",
            "type": "object"
          },
          "name": {
            "description": "The name restores and dry runs ask for the profile by.",
            "type": "string"
          },
          "network_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source network name or id to a network name or id here. Absent when empty.",
            "type": "object"
          },
          "origin_cloud": {
            "description": "The cloud whose backups the profile translates. Absent means any; a profile naming the backup's own cloud is preferred over one that names none.",
            "type": "string"
          },
          "recreate_security_groups": {
            "description": "When a security group has no entry and no namesake here, recreate it from the backed-up rules.",
            "type": "boolean"
          },
          "security_group_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source security group name to a security group name here. Absent when empty.",
            "type": "object"
          },
          "subnet_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source subnet name or id to a subnet id here. Absent when empty.",
            "type": "object"
          },
          "volume_type_map": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Source volume type to a volume type here. Absent when empty.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "RecoveryProfilePage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/RecoveryProfile"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "RecoveryReq": {
        "description": "A promise that the protected resources can be recovered at a site, and how fast.",
        "properties": {
          "rpo_target": {
            "description": "The most data loss promised, as a duration. Below the schedule's cadence only on the `hot` rung, and there not below the interval this cloud takes mirror snapshots at, where it takes them itself.",
            "type": "string"
          },
          "rto_target": {
            "description": "The longest recovery time promised, as a duration.",
            "type": "string"
          },
          "rung": {
            "description": "How recovery happens at the site. `cold` restores from the backups held there. `warm` starts from a standby kept ready there. `hot` starts from a copy mirrored there every few minutes. Required. The rung states the promise. Standbys and mirrors are set up separately.",
            "enum": [
              "cold",
              "warm",
              "hot"
            ],
            "type": "string"
          },
          "site_id": {
            "description": "The site recovery is promised at. Required. A repository the policy writes to, or a replica of one, must be reachable from it.",
            "type": "string"
          },
          "test": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RecoveryTestReq"
              }
            ],
            "description": "Optional. How often the recovery is to be tested."
          },
          "transport": {
            "description": "How the recoverable copy reaches the site. `rbd-mirror`, Ceph's own snapshot mirroring between the two clusters, carries the `hot` rung and nothing else. `repository`, the backups written where the site can read them, carries `cold` and `warm`. Optional: it defaults to the rung's own, and any other pairing is refused.",
            "enum": [
              "rbd-mirror",
              "repository"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "RecoveryTestReq": {
        "description": "How often a recovery promise is to be tested.",
        "properties": {
          "every": {
            "description": "The test cadence, as a duration. Required in this block. It is stated in the policy's `sentence`, but the service does not start the tests itself. The isolated network a test runs in is the recovery region's to choose, so this block names none.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RecoveryTestView": {
        "description": "How often a policy's recovery is tested in an isolated network.",
        "properties": {
          "every": {
            "description": "The interval between tests, in nanoseconds.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "RecoveryView": {
        "description": "What a policy provisions to recover its plans' resources at another site.",
        "properties": {
          "rpo_target": {
            "description": "The recovery point objective, in nanoseconds. Absent when not set.",
            "format": "int64",
            "type": "integer"
          },
          "rto_target": {
            "description": "The recovery time objective, in nanoseconds. Absent when not set.",
            "format": "int64",
            "type": "integer"
          },
          "rung": {
            "description": "How ready recovery is. `cold`: resources are restored there from the backups it can read. `warm`: a standby copy of each resource is kept there, updated as backups commit. `hot`: resources are mirrored there continuously.",
            "enum": [
              "cold",
              "warm",
              "hot"
            ],
            "type": "string"
          },
          "site_id": {
            "description": "The recovery site's id. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "site_name": {
            "description": "The recovery site's name. Absent when the site no longer exists.",
            "type": "string"
          },
          "test": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RecoveryTestView"
              }
            ],
            "description": "How often recovery is tested. Absent when it is not."
          },
          "transport": {
            "description": "How the recoverable copy reaches the site: `rbd-mirror` for the `hot` rung, `repository` for `cold` and `warm`.",
            "enum": [
              "rbd-mirror",
              "repository"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "RedeemHandoffReq": {
        "description": "A one-time sign-in code to redeem.",
        "properties": {
          "code": {
            "description": "The code from POST /v1/auth/handoff. Required.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RefusalView": {
        "description": "A resource or disk a plan cannot protect the usual way, and why.",
        "properties": {
          "detail": {
            "description": "The same, in words for people. May change between releases.",
            "type": "string"
          },
          "disposition": {
            "description": "What the service does about it. Absent means refused: the disk is not in the backup. `degraded` means it is protected the usual way, only less efficiently.",
            "enum": [
              "degraded"
            ],
            "type": "string"
          },
          "reason": {
            "description": "Why, as a stable code. `local_disk_ephemeral`: the disk is on a compute node's local storage. `non_rbd_backend`: the volume is on a storage backend the service cannot read. `luks_key_unavailable`: the volume is encrypted with a key the service cannot obtain. `image_not_found`, `image_unreadable`: the disk's storage image is missing or cannot be read. `image_mirror_non_primary`: the disk is the read-only copy that mirroring keeps of a volume whose primary is in another region, and is backed up there. `unsupported_image_features`: the image uses features the service cannot read. `coverage_unaccountable`: the disk could not be enumerated at all. `volume_detached_unreachable`: the volume is not attached and nothing can read it. `instance_mid_move`: the server is being resized or migrated. `instance_not_readable`: the server is shut off, shelved or suspended. `instance_rescued`: the server is booted from a rescue image. `no_ceph_connection`: the service has no connection to the storage cluster. `local_disk_via_compute`: an ephemeral disk beside a root disk that is read through the compute node's agent, which cannot read this disk. `object_gateway_unavailable`, `bucket_not_found`, `bucket_unreadable`: the bucket cannot be reached, does not exist, or cannot be read with the service's credential. `bucket_versioning_disabled`: the bucket is protected, but without versioning its changes are found less cheaply.",
            "enum": [
              "local_disk_ephemeral",
              "non_rbd_backend",
              "luks_key_unavailable",
              "image_not_found",
              "image_unreadable",
              "image_mirror_non_primary",
              "unsupported_image_features",
              "coverage_unaccountable",
              "volume_detached_unreachable",
              "instance_mid_move",
              "instance_not_readable",
              "instance_rescued",
              "no_ceph_connection",
              "local_disk_via_compute",
              "object_gateway_unavailable",
              "bucket_not_found",
              "bucket_unreadable",
              "bucket_versioning_disabled"
            ],
            "type": "string"
          },
          "resource_id": {
            "description": "The resource the refusal is about.",
            "type": "string"
          },
          "resource_kind": {
            "description": "The kind of that resource.",
            "enum": [
              "volume",
              "instance",
              "bucket"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "RefusalsResponse": {
        "description": "Everything a plan cannot protect the usual way, with the plan's full coverage account.",
        "properties": {
          "coverage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageView"
              }
            ],
            "description": "The plan's coverage claim, listing every disk it leaves out."
          },
          "plan_id": {
            "description": "The plan's id.",
            "type": "string"
          },
          "refusals": {
            "description": "One entry per resource and reason, as recorded when the resources were last inspected. A server with two refused disks for the same reason has one entry; `coverage` lists both. Empty when there are none.",
            "items": {
              "$ref": "#/components/schemas/RefusalView"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ReleaseBucket": {
        "description": "The locked bytes released in one month.",
        "properties": {
          "bytes": {
            "description": "How many locked bytes are released that month.",
            "format": "int64",
            "type": "integer"
          },
          "month": {
            "description": "The first day of the month, at midnight UTC.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ReleaseDayView": {
        "description": "Bytes whose lock lapses on one day.",
        "properties": {
          "bytes": {
            "description": "Bytes released that day.",
            "format": "int64",
            "type": "integer"
          },
          "day": {
            "description": "The day, as `YYYY-MM-DD` in UTC.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ReleaseDisk": {
        "description": "One mirrored image a hot release demoted, and the demotion snapshot the recovery region must hold before it may promote.",
        "properties": {
          "demoted_at": {
            "description": "When the image was demoted. Absent when not recorded.",
            "format": "date-time",
            "type": "string"
          },
          "demoted_snap_id": {
            "description": "The id of the demotion snapshot in the releasing region. The recovery region's copy of it names this id.",
            "minimum": 0,
            "type": "integer"
          },
          "global_id": {
            "description": "The image's mirror global id, which both regions share whatever the image is called.",
            "type": "string"
          },
          "image": {
            "description": "The image's name in the releasing region when it was demoted.",
            "type": "string"
          },
          "pool": {
            "description": "The pool the image is in.",
            "type": "string"
          },
          "source_resource_id": {
            "description": "The volume the image backs in the releasing region. Absent when not recorded.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ReleaseMember": {
        "description": "One member as a release stopped it and took its final backup.",
        "properties": {
          "disks": {
            "description": "For a hot group, each mirrored image the release demoted. Absent for warm and cold groups.",
            "items": {
              "$ref": "#/components/schemas/ReleaseDisk"
            },
            "type": "array"
          },
          "instance_id": {
            "description": "The server the releasing region stopped.",
            "type": "string"
          },
          "point_at": {
            "description": "When the final backup was committed. Absent when it was not recorded.",
            "format": "date-time",
            "type": "string"
          },
          "point_id": {
            "description": "The final backup, taken with the server off. Empty for a hot group, which hands over its demoted images instead.",
            "type": "string"
          },
          "source_instance_id": {
            "description": "The member's server id in the group's home.",
            "type": "string"
          },
          "stopped_at": {
            "description": "When that server was off. Absent when it was not recorded.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ReleaseProof": {
        "description": "A release the other region recorded, as a planned recover verified it.",
        "properties": {
          "cloud": {
            "description": "The cloud that released the workload.",
            "type": "string"
          },
          "epoch": {
            "description": "The custody epoch it released at.",
            "format": "int64",
            "type": "integer"
          },
          "members": {
            "description": "Each member as it was released.",
            "items": {
              "$ref": "#/components/schemas/ReleaseMember"
            },
            "type": "array"
          },
          "project_id": {
            "description": "The project the workload ran in, in the releasing region.",
            "type": "string"
          },
          "readiness_overridden": {
            "description": "True when the release went ahead without a fresh readiness report, by an operator's override. Absent otherwise.",
            "type": "boolean"
          },
          "recorded_at": {
            "description": "When the record was written.",
            "format": "date-time",
            "type": "string"
          },
          "released_at": {
            "description": "When the release was recorded.",
            "format": "date-time",
            "type": "string"
          },
          "run_id": {
            "description": "The releasing region's run.",
            "type": "string"
          },
          "rung": {
            "description": "The rung the releasing region recovers the group at.",
            "enum": [
              "cold",
              "warm"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "ReleaseRef": {
        "description": "The release a recover acts on, as the releasing region recorded it.",
        "properties": {
          "cloud": {
            "description": "The cloud that released the workload.",
            "type": "string"
          },
          "epoch": {
            "description": "The custody epoch it released at.",
            "format": "int64",
            "type": "integer"
          },
          "released_at": {
            "description": "When the release was recorded.",
            "format": "date-time",
            "type": "string"
          },
          "run_id": {
            "description": "The releasing region's run.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ReleaseReq": {
        "description": "A request to stop the workload here and hand it over to the other region.",
        "properties": {
          "override_recovery_readiness": {
            "description": "Release even though the other region has not reported recently that it can recover every member. Operator only.",
            "type": "boolean"
          },
          "reason": {
            "description": "Why, recorded with the run.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RemoteReadiness": {
        "description": "The newest readiness the recovery region reported for a group created here.",
        "properties": {
          "age_seconds": {
            "description": "How old it is, from `recorded_at` to now.",
            "format": "int64",
            "type": "integer"
          },
          "delegation": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DelegationState"
              }
            ],
            "description": "How the recovery region would act in the group's project there, as it reported it."
          },
          "epoch": {
            "description": "The custody epoch it was composed for.",
            "format": "int64",
            "type": "integer"
          },
          "fresh": {
            "description": "True when it is young enough for a release to rely on.",
            "type": "boolean"
          },
          "from_cloud": {
            "description": "The recovery region's cloud name.",
            "type": "string"
          },
          "from_site": {
            "description": "The recovery region's site. Absent when not reported.",
            "type": "string"
          },
          "issues": {
            "description": "What the recovery region reported in the way at the level of the whole group.",
            "items": {
              "$ref": "#/components/schemas/ReadinessIssue"
            },
            "type": "array"
          },
          "members": {
            "description": "Each member as the recovery region reported it.",
            "items": {
              "$ref": "#/components/schemas/MemberReadiness"
            },
            "type": "array"
          },
          "ready": {
            "description": "Whether the recovery region could recover every member when it composed it.",
            "type": "boolean"
          },
          "recorded_at": {
            "description": "When the recovery region composed it, by its own clock.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RepoHealth": {
        "description": "The last health check of one chunk repository.",
        "properties": {
          "canary_retained": {
            "description": "True when the storage locked the check's test object although no lock was asked for. Later checks of this repository only read. Absent means false.",
            "type": "boolean"
          },
          "checked_at": {
            "description": "When the check ran.",
            "format": "date-time",
            "type": "string"
          },
          "checks": {
            "description": "Each step of the check, in the order it ran.",
            "items": {
              "$ref": "#/components/schemas/Check"
            },
            "type": "array"
          },
          "driver": {
            "description": "The repository's driver.",
            "enum": [
              "s3",
              "rados",
              "posix"
            ],
            "type": "string"
          },
          "fallbacks": {
            "description": "What works less well on this storage because it lacks a feature, one sentence each. Absent when nothing does.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "immutability_verified": {
            "description": "True when the repository is registered with object lock and the storage confirmed that the lock is still configured. False when it is not, when the answer could not be read, and for repositories without object lock.",
            "type": "boolean"
          },
          "immutable": {
            "description": "True when the repository is registered with object lock, which the `object_lock` step checks the storage against.",
            "type": "boolean"
          },
          "latency_ns": {
            "description": "How long the whole check took, in nanoseconds. A check slower than the configured threshold reports `degraded`.",
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "description": "The repository's name.",
            "type": "string"
          },
          "readable": {
            "description": "Whether the repository served a read. A repository that can be read can still serve restores.",
            "type": "boolean"
          },
          "reason": {
            "description": "Why the status is not ok. Absent when it is.",
            "type": "string"
          },
          "repository_id": {
            "description": "The repository's id.",
            "type": "string"
          },
          "site_id": {
            "description": "The id of the site the repository is local to.",
            "type": "string"
          },
          "status": {
            "description": "`ok`: every step passed in time. `degraded`: working, but slow, unable to clean up after itself, or no longer enforcing the object lock it is registered with; backups continue. `down`: unreachable, refusing writes, or returning data it cannot read back correctly; no backup is sent to it, and restores continue while it can still be read. `unknown`: never checked, or the last check is too old to trust, and `reason` then says what it last found.",
            "enum": [
              "ok",
              "degraded",
              "down",
              "unknown"
            ],
            "type": "string"
          },
          "writable": {
            "description": "Whether the repository accepted the test write. Also false when no write was attempted, as `write_probed` then says.",
            "type": "boolean"
          },
          "write_probed": {
            "description": "False when the check deliberately did not write, because the bucket locks every object by default or locked an earlier test object. `writable` says nothing then.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "RepoHealthReport": {
        "description": "The last health check of every chunk repository at this site, with one overall verdict.",
        "properties": {
          "checked_at": {
            "description": "The time of the oldest check in the report: the report is only as fresh as that. Absent when the report lists no repository.",
            "format": "date-time",
            "type": "string"
          },
          "reason": {
            "description": "Each repository that is not ok and why, separated by semicolons, or why there is no verdict. Absent when every repository is ok.",
            "type": "string"
          },
          "repositories": {
            "description": "One entry per chunk repository at this site, ordered by name. rbd tiers are not checked.",
            "items": {
              "$ref": "#/components/schemas/RepoHealth"
            },
            "type": "array"
          },
          "status": {
            "description": "The worst status among the repositories, except that one repository down among several reports `degraded`. `unknown` when this site has no chunk repository or none has been checked. The statuses mean the same as on each repository.",
            "enum": [
              "ok",
              "degraded",
              "down",
              "unknown"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "RepoUsageResponse": {
        "description": "The operator's view of what a repository holds: its physical size, what each project references, the saving deduplication across projects brings, data under lock, and the state of cleanup. A figure that has not been measured is absent and named in `unavailable`.",
        "properties": {
          "ceph_trash_pinned_bytes": {
            "description": "Bytes of volumes Cinder deleted into the Ceph trash that cannot be purged because the service still holds a snapshot of them. Images in the trash the service holds nothing on are not counted. One figure for the whole cloud, repeated on every repository.",
            "format": "int64",
            "type": "integer"
          },
          "cross_tenant_margin": {
            "description": "`sum_tenant_referenced` minus `physical_bytes`, in bytes: the space saved because projects share identical blocks. Each project is billed on its own footprint, so this is space sold more than once and stored once. Chunk repositories only.",
            "format": "int64",
            "type": "integer"
          },
          "gc_degraded": {
            "description": "True when the last sweep of some dedup domain could not list every live backup. Nothing is deleted there until a sweep completes, and the footprint figures are those of the last complete sweep. Absent means false.",
            "type": "boolean"
          },
          "gc_last_completed_at": {
            "description": "When the last complete cleanup sweep finished. A degraded sweep does not move it.",
            "format": "date-time",
            "type": "string"
          },
          "gc_last_duration_seconds": {
            "description": "How long that sweep took, in seconds.",
            "format": "int64",
            "type": "integer"
          },
          "held_bytes": {
            "description": "Bytes of objects no backup references any more that cleanup could not delete because the storage's lock still holds them. Chunk repositories only.",
            "format": "int64",
            "type": "integer"
          },
          "held_objects": {
            "description": "How many objects `held_bytes` counts.",
            "format": "int64",
            "type": "integer"
          },
          "held_release": {
            "description": "When `held_bytes` is released, per day.",
            "items": {
              "$ref": "#/components/schemas/ReleaseDayView"
            },
            "type": "array"
          },
          "kind": {
            "description": "The repository's kind.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          },
          "leaked_disk_bytes": {
            "description": "Bytes of live Ceph images whose instance or volume OpenStack has deleted, left in the pool because the service held a snapshot of them when the delete ran: instance disks Nova could not remove, and volumes Cinder renamed to `<name>.deleted`. They stay until removed by hand, or, for instance disks, by the service when the manager's `ceph.remove_nova_orphans` is on. One figure for the whole cloud, repeated on every repository.",
            "format": "int64",
            "type": "integer"
          },
          "locked_bytes": {
            "description": "Bytes that backups reference and that object lock still holds, as of the last sweep. Chunk repositories with at least one measured project only.",
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "description": "The repository's name.",
            "type": "string"
          },
          "object_count": {
            "description": "How many objects the repository holds, measured together with `physical_bytes`.",
            "format": "int64",
            "type": "integer"
          },
          "physical_bytes": {
            "description": "What the repository occupies, in bytes, as measured by the last cleanup sweep of each of its dedup domains.",
            "format": "int64",
            "type": "integer"
          },
          "reclaimable_pending_bytes": {
            "description": "Bytes no backup references any more that cleanup has not deleted yet, because they are still inside the grace window or the last sweep was degraded. Chunk repositories only.",
            "format": "int64",
            "type": "integer"
          },
          "release_schedule": {
            "description": "When `locked_bytes` is released, summed per month.",
            "items": {
              "$ref": "#/components/schemas/ReleaseBucket"
            },
            "type": "array"
          },
          "repository_id": {
            "description": "The repository's id.",
            "type": "string"
          },
          "sum_tenant_referenced": {
            "description": "The sum of every project's `referenced_bytes`, in bytes. Chunk repositories only.",
            "format": "int64",
            "type": "integer"
          },
          "tenants": {
            "description": "What each project holds in the repository. Absent when no project holds anything.",
            "items": {
              "$ref": "#/components/schemas/TenantUsageView"
            },
            "type": "array"
          },
          "unavailable": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Each absent figure, mapped to why: `sweep_pending` when no sweep has measured it yet, `gc_degraded` when no sweep of this repository has completed and its sweeps are failing, `rbd_only_no_manifests` when an rbd tier has no deduplicated footprint to measure, `not_yet_verified` when no backup here has been read back yet, and `read_back_skipped` when read-back is skipped because the storage checks every write's checksum. Absent when every figure is present.",
            "type": "object"
          },
          "verified_coverage_pct_30d": {
            "description": "The percentage, from 0 to 100, of protected resources whose newest backup here was read back and verified within the verification window, 30 days by default. Absent before any backup here has been read back, and on a repository with object lock whose storage checks every write's checksum, where read-back is skipped. Chunk repositories only.",
            "type": "number"
          },
          "versions": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VersionInventoryView"
              }
            ],
            "description": "The last inventory of old object versions in a versioned bucket. Absent until one has been taken."
          }
        },
        "type": "object"
      },
      "RepositoryView": {
        "description": "A registered repository: where it is, how it is reached, its retention contract and what it can do. Access keys are never returned.",
        "properties": {
          "backup_diff_source": {
            "description": "For an rbd tier at this site, whether backups read changed blocks from the tier instead of from production storage. Absent for chunk repositories and for tiers at other sites.",
            "type": "boolean"
          },
          "bucket": {
            "description": "The S3 bucket. Absent for the other drivers.",
            "type": "string"
          },
          "ceph_conf": {
            "description": "The path of the Ceph configuration file the backup workers use to reach this rbd tier or rados pool. Absent when the workers' own configuration applies.",
            "type": "string"
          },
          "ceph_user": {
            "description": "The Ceph user the backup workers authenticate as for this rbd tier or rados pool. Absent when the workers' own Ceph user applies.",
            "type": "string"
          },
          "checksum_trailers": {
            "description": "True when the last health check found that the storage rejects a write whose checksum does not match its body. Writes then carry a checksum, and on a repository with object lock the periodic read-back of backups is skipped, because every stored write is already proven intact. Only measured on repositories with object lock. Absent means false.",
            "type": "boolean"
          },
          "chunk_size": {
            "description": "The size in bytes that data is cut into before deduplication. Fixed when the repository is registered. Absent on an rbd tier.",
            "format": "int64",
            "type": "integer"
          },
          "created_at": {
            "description": "When the repository was registered.",
            "format": "date-time",
            "type": "string"
          },
          "credentials_sealed": {
            "description": "True when the repository has access keys of its own, sealed by the key manager. False means the backup workers use whatever credentials their own environment provides.",
            "type": "boolean"
          },
          "driver": {
            "description": "How a chunk repository is stored: `s3` in an S3 bucket, `rados` in a Ceph pool, `posix` on a filesystem. Absent on an rbd tier.",
            "enum": [
              "s3",
              "rados",
              "posix"
            ],
            "type": "string"
          },
          "endpoint": {
            "description": "The S3 endpoint URL. Absent when not set.",
            "type": "string"
          },
          "id": {
            "description": "The repository's id.",
            "type": "string"
          },
          "instant_restore_reason": {
            "description": "Why `serves_instant_restore` is false, in words. Absent when it is true or absent.",
            "type": "string"
          },
          "kind": {
            "description": "`chunk` keeps deduplicated, compressed backups through a driver. `rbd` is an instant tier: it keeps recent backups as Ceph images and holds no chunks. `serves_instant_restore` says whether restores are cloned from it here.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          },
          "local": {
            "description": "True when the repository is at this service's site. A repository at another site is read from here to learn what that site writes, so one without access keys of its own carries a warning.",
            "type": "boolean"
          },
          "name": {
            "description": "The operator's name for the repository, unique in this cloud. Another cloud that restores from the same storage recognises it by this name.",
            "type": "string"
          },
          "namespace": {
            "description": "The RBD namespace inside the pool that an rbd tier creates its images in. Absent when the pool's default namespace is used.",
            "type": "string"
          },
          "object_lock": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RetentionContract"
              }
            ],
            "description": "The retention contract the storage enforces on every object written here. Absent when the repository has no object lock. Only the `s3` driver can have one."
          },
          "pool": {
            "description": "The Ceph pool of an rbd tier or of a rados repository. Absent for the other drivers.",
            "type": "string"
          },
          "prefix": {
            "description": "For the `s3` and `rados` drivers, the key prefix every object is written under. For the `posix` driver, the filesystem root. Absent when not set.",
            "type": "string"
          },
          "reachable_from_sites": {
            "description": "The ids of other sites whose backup workers can also reach this repository. A policy's recovery site must be able to read one of its repositories, and a restore in another cloud prefers a repository reachable from that cloud's sites. Absent when none.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "region": {
            "description": "The S3 region. Absent when not set.",
            "type": "string"
          },
          "replica_of": {
            "description": "The id of the repository this one is a replica of, kept in step by the storage's own replication. A replica can serve restores in place of the original. Absent when this repository is not a replica.",
            "type": "string"
          },
          "serves_instant_restore": {
            "description": "For an rbd tier at this site, whether this service clones restores from it into the pool Cinder serves and hands the volumes to their project. A policy promises restores in seconds only where this is true. Absent for chunk repositories and for tiers at other sites.",
            "type": "boolean"
          },
          "site_id": {
            "description": "The id of the site the repository is local to. Backup workers at this site read and write it.",
            "type": "string"
          },
          "soft_delete_grace": {
            "description": "How long, in nanoseconds, a backup or a plan that a project deletes stays hidden but recoverable by an operator before it is erased. On a repository with object lock, erasure also waits until the lock lapses. Absent means no grace: a deleted backup is erased as soon as it may be.",
            "format": "int64",
            "type": "integer"
          },
          "updated_at": {
            "description": "When the registration last changed.",
            "format": "date-time",
            "type": "string"
          },
          "warnings": {
            "description": "What the service cannot check for itself and the operator must know, one sentence each: on the answer to registering an rbd tier, how its pool must be used; on an `s3` repository at another site with no access keys of its own, that nothing has shown it can be read from here. Absent otherwise.",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "RepositoryViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/RepositoryView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "Resolution": {
        "description": "The result of resolving a backup against this cloud, and the plan a recovery would follow.",
        "properties": {
          "committable": {
            "description": "True when nothing is unresolved, before any confirmation is applied.",
            "type": "boolean"
          },
          "create_summary": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreateSummary"
              }
            ],
            "description": "The totals of `creates`, counted by kind, with the storage they take."
          },
          "creates": {
            "description": "Every object a recovery would create here.",
            "items": {
              "$ref": "#/components/schemas/PlannedCreate"
            },
            "type": "array"
          },
          "differs": {
            "description": "Elements that map, but differently from the source. Worth reading before committing.",
            "items": {
              "$ref": "#/components/schemas/MappingEntry"
            },
            "type": "array"
          },
          "plan": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RestorePlan"
              }
            ],
            "description": "The decisions a recovery would carry out."
          },
          "point_id": {
            "description": "The backup that was resolved.",
            "type": "string"
          },
          "profile_id": {
            "description": "The id of the stored profile used. Empty when the translation was given inline or none was used.",
            "type": "string"
          },
          "resolved": {
            "description": "Elements that map onto something here.",
            "items": {
              "$ref": "#/components/schemas/MappingEntry"
            },
            "type": "array"
          },
          "unresolved": {
            "description": "Elements that cannot be mapped. Each blocks a recovery until it is confirmed by name in `confirm_unresolved`.",
            "items": {
              "$ref": "#/components/schemas/MappingEntry"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ResourceCoverageOffer": {
        "description": "The partial protection a plan over this resource could accept: the disks in `covered` are protected, and the ones the digest accounts for are left out.",
        "properties": {
          "available": {
            "description": "True when at least one disk can be protected, so the offer can be accepted. False when none can, and the resource can only be refused.",
            "type": "boolean"
          },
          "digest": {
            "description": "Send this as `partial_coverage.digest`, with `accept` set to `true`, on a plan to accept leaving those disks out. It changes when the set of disks that cannot be protected changes.",
            "type": "string"
          },
          "holes": {
            "description": "How many disks would be left out at a cost to a restore. Entries in `excluded` that cost nothing, such as a config drive, are not counted.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "ResourceCoverageView": {
        "description": "One resource's account of what would and would not be protected.",
        "properties": {
          "covered": {
            "description": "The disks that would be protected. Empty for a bucket, which has no disks.",
            "items": {
              "$ref": "#/components/schemas/CoveredDiskView"
            },
            "type": "array"
          },
          "excluded": {
            "description": "The disks that would be left out, and why. Absent when there are none.",
            "items": {
              "$ref": "#/components/schemas/ExclusionView"
            },
            "type": "array"
          },
          "resource_id": {
            "description": "The resource's id, or a bucket's name.",
            "type": "string"
          },
          "resource_kind": {
            "description": "The kind of resource.",
            "enum": [
              "volume",
              "instance",
              "bucket"
            ],
            "type": "string"
          },
          "scope": {
            "description": "`partial` when this resource has a disk with data that would be left out, otherwise `full`.",
            "enum": [
              "full",
              "partial"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "ResourceView": {
        "description": "A server, volume or bucket in the project, and whether and how it can be protected.",
        "properties": {
          "covered": {
            "description": "The disks a plan would protect, as one consistent set for a server. A plan's `excluded_disks` takes an entry's `locator` to leave that disk out. Absent for a bucket.",
            "items": {
              "$ref": "#/components/schemas/CoveredDiskView"
            },
            "type": "array"
          },
          "degraded": {
            "description": "Buckets only. Conditions that leave nothing out but are worth knowing before choosing. Absent when there are none.",
            "items": {
              "$ref": "#/components/schemas/RefusalView"
            },
            "type": "array"
          },
          "excluded": {
            "description": "The disks a plan would leave out, each with why and what that costs a restore, including those left out at no cost, such as a config drive. Absent when there are none.",
            "items": {
              "$ref": "#/components/schemas/ExclusionView"
            },
            "type": "array"
          },
          "fast_diff": {
            "description": "Disks only. False when at least one disk lacks the storage's fast change tracking. Backups still work, but read the whole disk each time. Meaningful only on a resource that can be protected. Absent for a bucket.",
            "type": "boolean"
          },
          "id": {
            "description": "The resource's id; for a bucket, its name.",
            "type": "string"
          },
          "kind": {
            "description": "`volume` a block storage volume; `instance` a server with its disks; `bucket` an object storage bucket.",
            "enum": [
              "volume",
              "instance",
              "bucket"
            ],
            "type": "string"
          },
          "name": {
            "description": "The resource's name. Absent when it has none.",
            "type": "string"
          },
          "object_lock": {
            "description": "Buckets only. Whether the bucket enforces object lock retention, which a restore does not recreate. Absent for a disk.",
            "type": "boolean"
          },
          "partial_coverage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ResourceCoverageOffer"
              }
            ],
            "description": "Present when some of the resource's disks cannot be protected: the offer a plan can accept to protect the rest. Absent otherwise."
          },
          "protectable": {
            "description": "True when the resource can be protected whole. False can still mean it can be protected in part, as `partial_coverage` then says.",
            "type": "boolean"
          },
          "refusal": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RefusalView"
              }
            ],
            "description": "Why the resource cannot be protected whole. Absent when it can."
          },
          "size_bytes": {
            "description": "For a volume, its size. For a server, the total size of its disks. Always 0 for a bucket, whose size is not measured here.",
            "format": "int64",
            "type": "integer"
          },
          "versioned": {
            "description": "Buckets only. Whether object versioning is on. Without it each backup finds changes less precisely, but misses nothing. Absent for a disk.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "ResourceViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/ResourceView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "RestorePlan": {
        "description": "The decisions a recovery would carry out. Everything else, such as sizes, metadata and user data, comes from the backup itself.",
        "properties": {
          "availability_zone": {
            "description": "The server's availability zone here. Absent when unresolved or unrecorded.",
            "type": "string"
          },
          "drop_image": {
            "description": "True when no image here matches; the root disk is restored from the backup either way, so only the image reference is dropped.",
            "type": "boolean"
          },
          "drop_scheduler_hints": {
            "description": "Scheduler hints left out because they name objects in the source cloud.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "flavor_id": {
            "description": "The flavor the server would get here. Absent when unresolved.",
            "type": "string"
          },
          "flavor_name": {
            "description": "That flavor's name. Absent when unresolved.",
            "type": "string"
          },
          "floating_ips": {
            "description": "One entry per floating IP of the source server. Absent when there are none or no pool is set.",
            "items": {
              "$ref": "#/components/schemas/FloatingPlan"
            },
            "type": "array"
          },
          "image_id": {
            "description": "The image here matching the source's image. Absent when none matches or the source had none.",
            "type": "string"
          },
          "import_keypair": {
            "description": "True when that key pair is imported from the public key the backup captured.",
            "type": "boolean"
          },
          "keypair_name": {
            "description": "The key pair the server would get. Absent when it boots without one.",
            "type": "string"
          },
          "ports": {
            "description": "One entry per network attachment of the source server whose network resolved.",
            "items": {
              "$ref": "#/components/schemas/PortPlan"
            },
            "type": "array"
          },
          "security_groups": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SGPlan"
              }
            ],
            "description": "The security groups to reuse or recreate, in creation order."
          },
          "target_project_id": {
            "description": "The project the recovery would land in.",
            "type": "string"
          },
          "unresolved": {
            "description": "Always absent in a binding's dry run. In a recover's plan, the unresolved elements the group's registration accepted in `accept_gaps`, which the recover goes ahead without.",
            "items": {
              "$ref": "#/components/schemas/MappingEntry"
            },
            "type": "array"
          },
          "volumes": {
            "description": "One entry per volume in the backup.",
            "items": {
              "$ref": "#/components/schemas/VolumePlan"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "RestoreStageView": {
        "description": "One step of a restore.",
        "properties": {
          "detail": {
            "description": "What the step did, where its name does not say, such as which volume was attached where.",
            "type": "string"
          },
          "ended": {
            "description": "When the step ended. Absent while it is running. A restore that ends `failed` closes whatever step was still open at the restore's own end time, so `ended` is absent only on a restore that is itself still open.",
            "format": "date-time",
            "type": "string"
          },
          "name": {
            "description": "The step. `provisioning`: creating the target disks. `cloning_disks`: cloning them from the instant tier and handing them to the project. `creating_instance`: creating the new server. `waiting_for_instance`: waiting for it to be ready. `stopping_instance`: stopping the server so its disks can be written. `writing_disks`: writing the backup's data. `publishing_volumes`: making the restored volumes available. `attaching_volumes`: attaching them to the server. `starting_instance`: starting the server. `reassigning_addresses` is reserved and not recorded yet.",
            "enum": [
              "provisioning",
              "cloning_disks",
              "creating_instance",
              "waiting_for_instance",
              "stopping_instance",
              "writing_disks",
              "publishing_volumes",
              "attaching_volumes",
              "starting_instance",
              "reassigning_addresses"
            ],
            "type": "string"
          },
          "seconds": {
            "description": "How long the step took, or has taken so far.",
            "type": "number"
          },
          "started": {
            "description": "When the step started.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RestoreView": {
        "description": "A restore, or an attachment, with its state, what it has created and how long each step took.",
        "properties": {
          "actual_restore_seconds": {
            "description": "Seconds from the request to the end of the restore. Absent until it ends.",
            "format": "int64",
            "type": "integer"
          },
          "created_at": {
            "description": "When the restore was requested.",
            "format": "date-time",
            "type": "string"
          },
          "created_resources": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "What the restore created, keyed by what each thing replaces: `instance:<source server id>`, `port:<source port id>`, `floatingip:<source address>`, `bucket:<name>`, and for a disk its role and source id, such as `volume:<source volume id>`, each mapped to the id of the new resource. `bucket-pending-removal:<name>` is a bucket a failed restore is still taking away. Operators also see the service's own bookkeeping entries. Absent until something is created.",
            "type": "object"
          },
          "elevated": {
            "description": "True when the restore crossed a project boundary, because it lands in another project or was asked for from another project. Such restores are audited.",
            "type": "boolean"
          },
          "error": {
            "description": "Why the restore failed, or, while it is still open, why it is waiting. Absent otherwise. A project-scoped token sees a sentence with internal hostnames and process names taken out; an operator sees the text in full.",
            "type": "string"
          },
          "estimated_restore_seconds": {
            "description": "The estimate for the chosen copy when the restore was accepted, in seconds. 0 when there was none.",
            "format": "int64",
            "type": "integer"
          },
          "finished_at": {
            "description": "When the restore ended. Absent until then.",
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "The restore's id.",
            "type": "string"
          },
          "job_id": {
            "description": "The job carrying the restore out. On an attachment that is being detached, the detach job.",
            "type": "string"
          },
          "mapping_profile": {
            "description": "The recovery profile applied, when one was named.",
            "type": "string"
          },
          "mode": {
            "description": "What the restore does: `volume`, `instance`, `in_place` and `bucket` as requested, and `attach` for an attachment.",
            "enum": [
              "volume",
              "instance",
              "in_place",
              "bucket",
              "attach"
            ],
            "type": "string"
          },
          "point_id": {
            "description": "The backup being restored.",
            "type": "string"
          },
          "project_id": {
            "description": "The project that owns the backup.",
            "type": "string"
          },
          "requested_by": {
            "description": "The user who asked for the restore.",
            "type": "string"
          },
          "source_tier": {
            "description": "The copy the restore reads from: `chunk`, the repository copy, or `rbd`, the instant tier, whose disks are cloned rather than written.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          },
          "stages": {
            "description": "The steps the restore has taken, in order, with how long each took.",
            "items": {
              "$ref": "#/components/schemas/RestoreStageView"
            },
            "type": "array"
          },
          "state": {
            "description": "`pending`: queued. `running`: writing data or building resources. `succeeded`: done, and for an attachment, attached and usable. `failed`: stopped. `error` says why, and what it created is listed. `detaching`: an attachment being detached and deleted. `detached`: an attachment that is gone.",
            "enum": [
              "pending",
              "running",
              "succeeded",
              "failed",
              "detaching",
              "detached"
            ],
            "type": "string"
          },
          "target_project_id": {
            "description": "The project restored into.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RestoreViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/RestoreView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "ResyncReq": {
        "description": "A request to discard this region's divergence after backing it up.",
        "properties": {
          "confirm_discard_local_writes": {
            "description": "The acknowledgement that writes made here since the other region took over are set aside. Required.",
            "type": "boolean"
          },
          "reason": {
            "description": "Why, recorded with the run.",
            "type": "string"
          },
          "skip_divergence_point": {
            "description": "Discard what diverged here without backing it up first. Absent or false keeps it as a backup before anything is set aside.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "RetentionContract": {
        "description": "A repository's object-lock terms. Every object written to the repository is locked for at least `min_retention`, and the terms can be corrected only until `changeable_until`.",
        "properties": {
          "changeable_until": {
            "description": "Until this time the contract can be corrected or removed. After it the mode and the granularity are fixed, `min_retention` and `max_retention` can only rise, and the repository cannot be deregistered while it holds locked data.",
            "format": "date-time",
            "type": "string"
          },
          "granularity": {
            "description": "Retain-until dates are rounded up to a multiple of this, in nanoseconds, so that objects written close together share a date. Absent means the default of seven days.",
            "format": "int64",
            "type": "integer"
          },
          "max_retention": {
            "description": "The ceiling, in nanoseconds: no backup on this repository may be kept, and so locked, for longer. Absent means no ceiling.",
            "format": "int64",
            "type": "integer"
          },
          "min_retention": {
            "description": "The floor, in nanoseconds: every object written here is locked for at least this long. A policy that keeps backups here for less is refused.",
            "format": "int64",
            "type": "integer"
          },
          "mode": {
            "description": "`compliance`: nobody, the storage's administrators included, can delete an object before its retain-until date. `governance`: a storage user with bypass permission can.",
            "enum": [
              "compliance",
              "governance"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "RetirePolicyResp": {
        "description": "A retired policy, as an operator sees it, with what happened to its plans.",
        "properties": {
          "assigned_to": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PolicyAssignment"
              }
            ],
            "description": "The domains and projects the policy is assigned to. Operators only.",
            "x-operator-only": true
          },
          "created_at": {
            "description": "When the policy was created.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "Free text the operator wrote. Absent when empty.",
            "type": "string"
          },
          "id": {
            "description": "The policy's id.",
            "type": "string"
          },
          "mandatory": {
            "description": "True when a project could not pause or delete protection under this policy.",
            "type": "boolean"
          },
          "manual": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ManualView"
              }
            ],
            "description": "How long a manual backup under this policy could be kept. Absent when the vault allows no manual backups, or there is no vault."
          },
          "moved_plans": {
            "description": "How many plans were moved onto the successor, deleted plans still inside their purge grace included.",
            "type": "integer"
          },
          "name": {
            "description": "The policy's name, unique across the cloud.",
            "type": "string"
          },
          "plan_count": {
            "description": "Always 0, since a retired policy has no plans. Operators only.",
            "type": "integer",
            "x-operator-only": true
          },
          "sentence": {
            "description": "What the policy promised, as a paragraph of English for people. The wording may change between releases.",
            "type": "string"
          },
          "spec": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PolicySpecView"
              }
            ],
            "description": "The policy's terms. Durations are integers in nanoseconds."
          },
          "state": {
            "description": "Always `retired` after this call.",
            "enum": [
              "active",
              "deprecated",
              "retired"
            ],
            "type": "string"
          },
          "unsatisfied": {
            "description": "Why the policy could not be honoured, one code each, as on the policy itself. Absent when it could.",
            "items": {
              "enum": [
                "backup_repository_gone",
                "backup_repository_not_a_chunk_tier",
                "instant_repository_gone",
                "instant_repository_not_an_rbd_tier",
                "copy_repository_gone",
                "copy_repository_not_a_chunk_tier",
                "retention_below_repository_floor",
                "retention_above_repository_ceiling",
                "recovery_site_gone",
                "recovery_site_cannot_read_any_copy",
                "repository_unavailable"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "updated_at": {
            "description": "When the policy was retired.",
            "format": "date-time",
            "type": "string"
          },
          "vault": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VaultRefView"
              }
            ],
            "description": "The vault on the backup repository. Absent when that repository has no vault."
          },
          "visibility": {
            "description": "`public`: offered to every project. `assigned`: offered only to the domains and projects in `assigned_to`.",
            "enum": [
              "public",
              "assigned"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "RetireReq": {
        "description": "A request to remove what a superseded recovery left here.",
        "properties": {
          "reason": {
            "description": "Why, recorded with the run.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RetiredResource": {
        "description": "A resource taken out of a plan whose backups are still kept.",
        "properties": {
          "leaves_on": {
            "description": "When the last of its backups expires or can be erased. Absent when not known.",
            "format": "date-time",
            "type": "string"
          },
          "points": {
            "description": "How many of its backups are still kept.",
            "type": "integer"
          },
          "resource_id": {
            "description": "The resource's id, or a bucket's name.",
            "type": "string"
          },
          "retired_at": {
            "description": "When it was taken out of the plan.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RetiredView": {
        "description": "A resource taken out of a plan under a mandatory policy whose backups are still kept.",
        "properties": {
          "leaves_on": {
            "description": "When the last of its backups expires or can be erased. Absent when not known.",
            "format": "date-time",
            "type": "string"
          },
          "name": {
            "description": "The plan's name.",
            "type": "string"
          },
          "plan_id": {
            "description": "The plan's id.",
            "type": "string"
          },
          "points": {
            "description": "How many of its backups are still kept.",
            "type": "integer"
          },
          "policy_name": {
            "description": "The mandatory policy's name.",
            "type": "string"
          },
          "resource_id": {
            "description": "The resource that was taken out.",
            "type": "string"
          },
          "retired_at": {
            "description": "When it was taken out.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ReturnDisk": {
        "description": "A disk of an original server that a failback writes back into.",
        "properties": {
          "resource_id": {
            "description": "The volume's id, or the server's for its root disk.",
            "type": "string"
          },
          "role": {
            "description": "Whether the disk is the server's `root` disk or a `volume`.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "RunMeasured": {
        "description": "What a disaster recovery run measured. Each figure is absent until it was measured.",
        "properties": {
          "data_loss_seconds": {
            "description": "For a planned recover, 0 when it was proved that nothing was lost. For an unplanned one, an upper bound on how much was: seconds from the oldest member's recovery point to the run's admission.",
            "format": "int64",
            "type": "integer"
          },
          "downtime_seconds": {
            "description": "For a planned recover, seconds from the release's first member stop to the last member healthy here.",
            "format": "int64",
            "type": "integer"
          },
          "recovery_point_at": {
            "description": "The instant the recovered data is from, for the member whose data is oldest.",
            "format": "date-time",
            "type": "string"
          },
          "recovery_point_newest_at": {
            "description": "The instant the recovered data is from, for the member whose data is newest.",
            "format": "date-time",
            "type": "string"
          },
          "recovery_point_spread_seconds": {
            "description": "Seconds between `recovery_point_at` and `recovery_point_newest_at`: how far apart in time the members' data is. 0 when every member recovered to the same instant.",
            "format": "int64",
            "type": "integer"
          },
          "rto_seconds": {
            "description": "Seconds from the run's admission to its last member passing its health check. For a test, building the isolated network counts and tearing it down does not.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "RunMember": {
        "description": "One group member's progress through a recover or a test, as the run's journal records it.",
        "properties": {
          "addresses": {
            "description": "The fixed addresses the member's server got. Absent until it has booted.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "boot_order": {
            "description": "The member's boot tier.",
            "type": "integer"
          },
          "booted_at": {
            "description": "When the member's server was up. Absent until then.",
            "format": "date-time",
            "type": "string"
          },
          "error": {
            "description": "Why the member's last failed step failed. Absent unless `state` is `failed`.",
            "type": "string"
          },
          "healthy_at": {
            "description": "When the member passed its health check. Absent until then.",
            "format": "date-time",
            "type": "string"
          },
          "instance_id": {
            "description": "The server the run brought up for the member: in the group's project for a recover, in the test policy's project for a test. Absent until it is created.",
            "type": "string"
          },
          "materialised_at": {
            "description": "When the member's disks were ready. Absent until then.",
            "format": "date-time",
            "type": "string"
          },
          "name": {
            "description": "The member's display name. Absent when none.",
            "type": "string"
          },
          "point_id": {
            "description": "The backup the member was recovered from. Absent until it is chosen.",
            "type": "string"
          },
          "recovery_point_at": {
            "description": "The instant the member's data is from. Absent until it is known.",
            "format": "date-time",
            "type": "string"
          },
          "source_instance_id": {
            "description": "The member's server id in the group's home, as the group lists it.",
            "type": "string"
          },
          "state": {
            "description": "`pending`: nothing has been done for the member yet. `materialised`: its disks exist, restored from a backup or cloned from its standby. `booted`: its server is up. `healthy`: it passed its health check, or booted when it has none. `failed`: a step for it failed; `error` says why.",
            "enum": [
              "pending",
              "materialised",
              "booted",
              "healthy",
              "failed"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "RunResponse": {
        "description": "A disaster recovery run after a request about it was accepted.",
        "properties": {
          "job_id": {
            "description": "The job that carries the run out. Absent when it has none.",
            "type": "string"
          },
          "run": {
            "allOf": [
              {
                "$ref": "#/components/schemas/DRRun"
              }
            ],
            "description": "The run, as it stands after the request."
          }
        },
        "type": "object"
      },
      "SGGroup": {
        "description": "One source security group and what a recovery would do with it.",
        "properties": {
          "action": {
            "description": "`reuse`: a group here, by name or by the profile, is used as it stands and not changed. `create`: the group is recreated from its rules. `unresolved`: neither is possible.",
            "enum": [
              "reuse",
              "create",
              "unresolved"
            ],
            "type": "string"
          },
          "deferred_rules": {
            "description": "Rules that close a cycle between groups. They are left out of the recreated group.",
            "items": {
              "$ref": "#/components/schemas/SGRule"
            },
            "type": "array"
          },
          "reason": {
            "description": "For `unresolved`, why. Absent otherwise.",
            "type": "string"
          },
          "rules": {
            "description": "Rules added when the group is created whose remote groups exist by then.",
            "items": {
              "$ref": "#/components/schemas/SGRule"
            },
            "type": "array"
          },
          "source_id": {
            "description": "The group's id in the source cloud.",
            "type": "string"
          },
          "source_name": {
            "description": "The group's name in the source cloud.",
            "type": "string"
          },
          "target_id": {
            "description": "For `reuse`, the id of the group here. Absent otherwise.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "SGPlan": {
        "description": "How the source server's security groups map here, in the order they would be created.",
        "properties": {
          "groups": {
            "description": "Each source security group and what happens to it.",
            "items": {
              "$ref": "#/components/schemas/SGGroup"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "SGRule": {
        "description": "How one rule's reference to another security group is resolved.",
        "properties": {
          "remote_ref": {
            "description": "The source group the rule refers to, when that group is itself being recreated. Absent otherwise.",
            "type": "string"
          },
          "remote_target_id": {
            "description": "The id of an existing group here the rule refers to. Absent otherwise.",
            "type": "string"
          },
          "rule_index": {
            "description": "The rule's position among the source group's rules.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "SLA": {
        "description": "A disaster recovery plan's measured protection over the last 30 days: how recent its newest backup is against the target, how reliably backups succeed, since when backups can be restored, and how its replication and tests perform.",
        "properties": {
          "actual_rpo": {
            "description": "Nanoseconds since the group's newest committed backup. 0 when there is none, and then listed in `unavailable`.",
            "format": "int64",
            "type": "integer"
          },
          "consecutive_failures": {
            "description": "How many of the most recent concluded backup runs failed in a row.",
            "type": "integer"
          },
          "immutably_protected_since": {
            "description": "Since when a backup under an object lock exists, which no one can erase before the lock ends. Absent when there is none.",
            "format": "date-time",
            "type": "string"
          },
          "measured_rto": {
            "description": "The recovery time measured by the group's last successful test, in nanoseconds. Absent before the first one.",
            "format": "int64",
            "type": "integer"
          },
          "oldest_restorable_point": {
            "description": "The oldest committed backup of the group. Absent when there is none.",
            "format": "date-time",
            "type": "string"
          },
          "recent_test_rtos": {
            "description": "The recovery times of the group's recent successful tests, newest first, in seconds. Absent before the first one.",
            "items": {
              "format": "int64",
              "type": "integer"
            },
            "type": "array"
          },
          "replication_lag": {
            "description": "The worst replication lag of the group's mirrored images, in nanoseconds. Absent when no mirrored image is tracked.",
            "format": "int64",
            "type": "integer"
          },
          "rpo_compliance_pct": {
            "description": "The percentage, 0 to 100, of the last 30 days during which the newest backup was no older than `rpo_target`.",
            "type": "number"
          },
          "rpo_target": {
            "description": "The recovery point objective the group's policy promises, in nanoseconds. 0 when it promises none, and then listed in `unavailable`.",
            "format": "int64",
            "type": "integer"
          },
          "rto_target": {
            "description": "The recovery time objective the group's policy promises, in nanoseconds. Absent when it promises none.",
            "format": "int64",
            "type": "integer"
          },
          "success_rate_30d": {
            "description": "The percentage, 0 to 100, of backup runs concluded in the last 30 days that succeeded.",
            "type": "number"
          }
        },
        "type": "object"
      },
      "ScheduleReq": {
        "description": "When backups run: `every` or `cron`, not both. Times of day are `HH:MM` in the cloud's local time zone, which the operator sets.",
        "properties": {
          "at": {
            "description": "The time of day the cadence is aligned to. A cadence of a day or more runs at it, and a shorter one runs at it and every cadence after. Not allowed with `cron`.",
            "type": "string"
          },
          "cron": {
            "description": "A standard five-field cron expression, in place of `every`. Cannot be combined with `every` or `at`. Projects are not shown it.",
            "type": "string"
          },
          "every": {
            "description": "The cadence, from `15m` to `168h`. Below a day it must divide 24 hours evenly. From a day up it must be a whole number of days, counted from 1 January 1970, so a weekly cadence runs on Thursdays. Required unless `cron` is given.",
            "type": "string"
          },
          "jitter": {
            "description": "Up to this long is added to each start, as a fixed offset per plan, so plans on the same schedule do not all start together.",
            "type": "string"
          },
          "start_within": {
            "description": "How long after its scheduled time a backup may still start. One that cannot start in time, for example because its window is closed, is skipped and recorded as missed. Absent means no limit.",
            "type": "string"
          },
          "window_end": {
            "description": "The end of the window. It may be earlier than `window_start`, for a window across midnight, but it may not equal it.",
            "type": "string"
          },
          "window_start": {
            "description": "The start of the window backups may start in. Needs `window_end`. A backup due outside the window waits for it to open.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ScheduleView": {
        "description": "When a policy's backups run. Times of day are in the service's configured time zone.",
        "properties": {
          "at": {
            "description": "The time of day, `HH:MM`, a daily or longer interval starts at. Absent when not set.",
            "type": "string"
          },
          "cron": {
            "description": "The schedule as a five-field cron expression, when it is one. A project-scoped token sees neither this nor `every` for such a schedule; `sentence` describes it. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "every": {
            "description": "The interval between backups, in nanoseconds, from 15 minutes to 7 days. Absent when the schedule is a cron expression.",
            "format": "int64",
            "type": "integer"
          },
          "jitter": {
            "description": "The most a plan's backups are delayed after their scheduled time, in nanoseconds, so that plans on the same schedule do not all start at once. Each plan gets a fixed delay of its own. Absent when 0.",
            "format": "int64",
            "type": "integer"
          },
          "start_within": {
            "description": "How long after its scheduled time a backup may still start, in nanoseconds. One that cannot start in time is skipped and recorded as a missed backup. Absent when there is no limit.",
            "format": "int64",
            "type": "integer"
          },
          "window_end": {
            "description": "The time of day, `HH:MM`, the backup window closes.",
            "type": "string"
          },
          "window_start": {
            "description": "The time of day, `HH:MM`, the backup window opens. Backups start only inside the window, which may span midnight. Absent when backups may start at any time.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ScopeSessionReq": {
        "description": "The scope to exchange the caller's token for. Name exactly one of `project_id` and `system`.",
        "properties": {
          "project_id": {
            "description": "The project to scope the new token to. The user must hold a role on it.",
            "type": "string"
          },
          "system": {
            "description": "`true` for a token scoped to the whole cloud, which operator calls need. The user must hold a role on the cloud itself.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "ScopedSession": {
        "description": "A new token and its scope. A sign-in that named no project answers an unscoped token, marked `unscoped`, with the projects it can be scoped to; exchange it with POST /v1/auth/session/scope before calling anything outside this group.",
        "properties": {
          "expires_at": {
            "description": "When the token expires.",
            "format": "date-time",
            "type": "string"
          },
          "project_id": {
            "description": "The project the token is scoped to. Absent on an unscoped or a cloud-scoped token.",
            "type": "string"
          },
          "project_name": {
            "description": "The name of that project. Absent when `project_id` is.",
            "type": "string"
          },
          "projects": {
            "description": "On an unscoped sign-in only: the projects the token can be scoped to, sorted by name. Absent when there are none, or when the service could not list them. Then sign in again naming a project.",
            "items": {
              "$ref": "#/components/schemas/ProjectRef"
            },
            "type": "array"
          },
          "roles": {
            "description": "The roles the token carries in its scope. Absent on an unscoped token.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "system": {
            "description": "On an unscoped sign-in only: true when the token can also be scoped to the whole cloud. Absent otherwise, including on a scope exchange that asked for the whole cloud.",
            "type": "boolean"
          },
          "token": {
            "description": "The token. Send it in `X-Auth-Token` on later calls, and keep it secret.",
            "type": "string"
          },
          "unscoped": {
            "description": "True when the token is scoped to nothing. Absent otherwise. Only a sign-in can answer it, because a scope exchange always asks for a scope.",
            "type": "boolean"
          },
          "user_name": {
            "description": "The name of the signed-in user.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "Session": {
        "description": "The session a one-time sign-in code carried: the token of the user who created the code, with the same scope, roles and expiry.",
        "properties": {
          "expires_at": {
            "description": "When the token expires. This is the token's expiry, not the code's.",
            "format": "date-time",
            "type": "string"
          },
          "project_id": {
            "description": "The project the token is scoped to. Absent on a cloud-scoped token.",
            "type": "string"
          },
          "project_name": {
            "description": "The name of that project. Absent when `project_id` is.",
            "type": "string"
          },
          "roles": {
            "description": "The roles the token carries.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "token": {
            "description": "The user's token. Send it in `X-Auth-Token` on later calls, and keep it secret.",
            "type": "string"
          },
          "user_name": {
            "description": "The name of the user.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "SessionDetail": {
        "description": "What a browse session has found on the backup's disks and what it has mounted.",
        "properties": {
          "deadline": {
            "description": "When the session is closed whatever happens.",
            "format": "date-time",
            "type": "string"
          },
          "disks": {
            "description": "Only when the session is opened: each disk the session reads, and how much of it has been fetched from the backup so far. Absent on later reads of the session.",
            "items": {
              "$ref": "#/components/schemas/DiskRead"
            },
            "type": "array"
          },
          "filesystems": {
            "description": "Every filesystem found on the backup's disks, including LVM logical volumes.",
            "items": {
              "$ref": "#/components/schemas/Filesystem"
            },
            "type": "array"
          },
          "lvm": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LVMInfo"
              }
            ],
            "description": "The LVM volumes found on the disks. Empty when there are none."
          },
          "mounts": {
            "description": "What is mounted now. Empty when nothing is.",
            "items": {
              "$ref": "#/components/schemas/Mount"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "SessionProjectsView": {
        "description": "The projects a token can be scoped to.",
        "properties": {
          "projects": {
            "description": "The projects, sorted by name. Empty when there are none.",
            "items": {
              "$ref": "#/components/schemas/ProjectRef"
            },
            "type": "array"
          },
          "system": {
            "description": "Whether the token's user can also be scoped to the whole cloud, which operator calls need.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "SessionView": {
        "description": "Who the caller's token belongs to and what it may open, as a console needs it to decide what to show.",
        "properties": {
          "expires_at": {
            "description": "When the token expires.",
            "format": "date-time",
            "type": "string"
          },
          "grants": {
            "additionalProperties": {
              "type": "boolean"
            },
            "description": "Whether the token may perform each action a console shows differently. It is judged against the token's own project and is not narrowed by `scope=tenant`. `repository:usage` is the action that opens the operator calls. `dr:recover:unplanned` is recovering a protection group without the other region's release. `dr:run:abort` is stopping a disaster recovery run past its point of no return. `quota:write` is setting quotas. `dr:release:override` is releasing a group without the recovery region's readiness. `dr:resync` is discarding a region's divergence. `dr:split_brain:acknowledge` is keeping this region's copy after a split brain. `protection_group:register_recovery` is registering a recovery and creating failback bindings.",
            "type": "object"
          },
          "operator": {
            "description": "True when the token may use the operator calls. Always `false` on a request that carries `scope=tenant`, which asks for what the project itself sees.",
            "type": "boolean"
          },
          "project_id": {
            "description": "The project the token is scoped to. Absent on a cloud-scoped token.",
            "type": "string"
          },
          "project_name": {
            "description": "The name of that project. Absent when `project_id` is.",
            "type": "string"
          },
          "roles": {
            "description": "The roles the token carries. Empty, never absent, for a token with none.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "system_scope": {
            "description": "True when the token is scoped to the whole cloud rather than to a project.",
            "type": "boolean"
          },
          "user_id": {
            "description": "The id of the user the token belongs to.",
            "type": "string"
          },
          "user_name": {
            "description": "The user's name.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "SimulateRetentionReq": {
        "description": "The policy to preview and, optionally, the retention to preview it with.",
        "properties": {
          "at": {
            "description": "The time to evaluate at, as an RFC 3339 time. Defaults to now. Backups committed after it are left out.",
            "format": "date-time",
            "type": "string"
          },
          "plan_id": {
            "description": "Only this plan's backups. The plan must follow the policy.",
            "type": "string"
          },
          "policy_id": {
            "description": "Required. The protection policy whose plans' backups are evaluated.",
            "type": "string"
          },
          "spec": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PolicySpecReq"
              }
            ],
            "description": "A complete policy spec to evaluate instead of the policy's current one, in the form used to create a policy. Its `keep` ladder must retain something."
          }
        },
        "type": "object"
      },
      "SimulateRetentionResp": {
        "description": "The outcome of a retention preview over the backups the policy's plans hold. Manual backups taken inside a plan are left out: they keep the expiry they were taken with, whatever the ladder says.",
        "properties": {
          "expired": {
            "description": "How many backups would expire.",
            "type": "integer"
          },
          "kept": {
            "description": "How many backups are kept, counting the locked ones.",
            "type": "integer"
          },
          "locked": {
            "description": "How many backups are locked past the evaluated time, and so kept whatever the ladder says.",
            "type": "integer"
          },
          "method": {
            "description": "How the reclaimable figure was found. `manifest_walk`: exactly, in `reclaimable_bytes`. `sum_exclusive_lower_bound`: as a lower bound, in `reclaimable_lower_bound_bytes`. `unavailable`: neither could be computed.",
            "enum": [
              "manifest_walk",
              "sum_exclusive_lower_bound",
              "unavailable"
            ],
            "type": "string"
          },
          "note": {
            "description": "A sentence explaining the figure when it is zero, a lower bound or unavailable. Absent otherwise.",
            "type": "string"
          },
          "plan_id": {
            "description": "The plan the preview was narrowed to. Absent when every plan of the policy was evaluated.",
            "type": "string"
          },
          "points": {
            "description": "The verdict on each backup, newest first.",
            "items": {
              "$ref": "#/components/schemas/SimulatedPoint"
            },
            "type": "array"
          },
          "policy_id": {
            "description": "The policy that was evaluated.",
            "type": "string"
          },
          "reclaimable_bytes": {
            "description": "The bytes that expiring those backups would free, found by walking their manifests. Each expiring backup is measured in the repository it is stored in, against the remaining backups of its own project, so this is what the projects would stop referencing, not what the repository would physically free. Absent when no exact figure is available.",
            "format": "int64",
            "type": "integer"
          },
          "reclaimable_lower_bound_bytes": {
            "description": "When there is no exact figure, the sum of the expiring backups' exclusive bytes. It is a lower bound, because blocks shared only among the expiring backups are exclusive to none of them. Absent otherwise.",
            "format": "int64",
            "type": "integer"
          },
          "shortens": {
            "description": "True when the evaluated ladder would expire at least one backup earlier than its current expiry date.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "SimulatedPoint": {
        "description": "What the evaluated retention does to one backup.",
        "properties": {
          "committed_at": {
            "description": "When the backup was committed.",
            "format": "date-time",
            "type": "string"
          },
          "expires_at": {
            "description": "When the evaluated ladder expires the backup.",
            "format": "date-time",
            "type": "string"
          },
          "kept": {
            "description": "True when the evaluated ladder still keeps the backup at the evaluated time.",
            "type": "boolean"
          },
          "kept_by": {
            "description": "The widest rung of the ladder the backup holds, or `all` when it holds none of the calendar rungs. Absent when the backup is not kept.",
            "enum": [
              "yearly",
              "monthly",
              "weekly",
              "daily",
              "all"
            ],
            "type": "string"
          },
          "locked_until": {
            "description": "The storage keeps the backup locked until this time, whatever the ladder says. Absent when it is not locked.",
            "format": "date-time",
            "type": "string"
          },
          "point_id": {
            "description": "The backup's id.",
            "type": "string"
          },
          "was": {
            "description": "The backup's current expiry date, before the change. Absent when it has none.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "SiteView": {
        "description": "A site a protection policy's recovery section can name, or the one a protection group runs at.",
        "properties": {
          "id": {
            "description": "The site's id, as a policy's `recovery.site_id` takes it and a group's `site_id` reports it.",
            "type": "string"
          },
          "local": {
            "description": "True for the site this service runs at.",
            "type": "boolean"
          },
          "name": {
            "description": "The site's name.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "SiteViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/SiteView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "SlaCoverage": {
        "description": "What a plan's measurements are about.",
        "properties": {
          "covered_immutable_since": {
            "description": "For a partial plan, `immutably_protected_since` restated for the disks it covers.",
            "format": "date-time",
            "type": "string"
          },
          "covered_restorable_since": {
            "description": "For a partial plan, `oldest_restorable_point` restated for the disks it covers, since no backup restores the whole resource.",
            "format": "date-time",
            "type": "string"
          },
          "excluded": {
            "description": "For a partial plan, the disks it leaves out. The plan's `coverage` says who accepted that and when.",
            "items": {
              "$ref": "#/components/schemas/ExclusionView"
            },
            "type": "array"
          },
          "resources": {
            "description": "How many resources the plan names.",
            "type": "integer"
          },
          "scope": {
            "description": "`partial` when the plan's accepted coverage leaves out a disk with data, otherwise `full`.",
            "enum": [
              "full",
              "partial"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "SlaResponse": {
        "description": "What a protection group's protection has actually achieved over the last 30 days. A figure with no measurement is listed under `unavailable` instead of being reported as zero.",
        "properties": {
          "group_id": {
            "description": "The group these figures are for.",
            "type": "string"
          },
          "sla": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SLA"
              }
            ],
            "description": "The figures. `actual_rpo`, `oldest_restorable_point`, `rpo_compliance_pct` and `immutably_protected_since` count the committed backups of the group's member servers, in the group's project, whichever plan took them. `consecutive_failures` and `success_rate_30d` cover every backup in the group's project, not only the members'. `replication_lag` is the worst lag among the members' mirrored disks. `measured_rto` and `recent_test_rtos` come from successful tests that recorded a recovery time, newest first: on a recovery, its own test runs; at the group's home, the test results the recovery region writes back into the repository they share, as this region has read them. `rpo_target` and `rto_target` are the group's policy's recovery section; `rto_target` is absent when the policy states none. `rpo_compliance_pct` is the share of gaps between consecutive backups in the last 30 days, plus the gap from the newest backup to now, that stayed within `rpo_target`."
          },
          "unavailable": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Each figure that has no measurement, mapped to why. The keys are `rpo_target`, `actual_rpo`, `oldest_restorable_point`, `immutably_protected_since`, `measured_rto` and `replication_lag`. `rpo_target` is listed when the group names no policy, when its policy is gone or states no recovery, or when the policy states no recovery point objective. Absent when everything was measured.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "Standby": {
        "description": "A copy of a protected resource's disks kept in a pool and brought up to date after each new backup, so it can become volumes in about the time a boot takes.",
        "properties": {
          "applied_at": {
            "description": "When the copy last finished an update. Absent until the first one.",
            "format": "date-time",
            "type": "string"
          },
          "applied_point_id": {
            "description": "The backup the copy holds. Absent until the first update finishes.",
            "type": "string"
          },
          "applied_snap": {
            "description": "The snapshot of every disk taken when the update to `applied_point_id` finished. A promote clones this snapshot, never the disks as they are, which an update in progress or one that failed part way leaves half written. Absent until the first update finishes, or on a standby registered before this was recorded until it is next found current; without it the standby cannot be promoted.",
            "type": "string"
          },
          "created_at": {
            "description": "When the standby was registered.",
            "format": "date-time",
            "type": "string"
          },
          "domain_id": {
            "description": "The domain of that project.",
            "type": "string"
          },
          "error": {
            "description": "Why the last update failed. Present only in `degraded`.",
            "type": "string"
          },
          "group_id": {
            "description": "The protection group given at registration. Absent when none was given. Recorded only.",
            "type": "string"
          },
          "hold_run_id": {
            "description": "The disaster recovery run currently cloning from the standby. While it is set no update starts and no earlier snapshot is removed. Absent when nothing holds it.",
            "type": "string"
          },
          "id": {
            "description": "The standby's id.",
            "type": "string"
          },
          "project_id": {
            "description": "The project the standby belongs to: the one named when it was registered, or the binding's target project for a standby a recovery binding created.",
            "type": "string"
          },
          "resource_id": {
            "description": "The protected resource whose backups the standby copies.",
            "type": "string"
          },
          "site_id": {
            "description": "The site the copy is kept at. Only that site updates it.",
            "type": "string"
          },
          "state": {
            "description": "`seeding`: has never held a backup, and the first update copies every disk in full. `replaying`: an update is running. `current`: holds the newest backup of its resource. `degraded`: the last update failed. It still holds `applied_point_id` and its `applied_snap`, older than the newest backup, and the next pass tries again. A standby in any state with an `applied_snap` can be promoted, to `applied_point_id`.",
            "enum": [
              "seeding",
              "replaying",
              "current",
              "degraded"
            ],
            "type": "string"
          },
          "target_pool": {
            "description": "The pool the copy is kept in.",
            "type": "string"
          },
          "target_prefix": {
            "description": "Put before each disk's id to name its image in the pool. Absent when empty, in which case the images are named by the disk ids alone.",
            "type": "string"
          },
          "updated_at": {
            "description": "When the standby last changed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "StandbyPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/Standby"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "StandbyReq": {
        "description": "Where a recovery keeps each member's standby.",
        "properties": {
          "pool": {
            "description": "The pool the standbys are kept in. Required.",
            "type": "string"
          },
          "prefix": {
            "description": "Put before each disk's id to name a standby's images.",
            "type": "string"
          },
          "site_id": {
            "description": "The site the standbys are kept at. Defaults to this site.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "TargetProgress": {
        "description": "How far a job has got on one disk or bucket.",
        "properties": {
          "bytes_done": {
            "description": "Bytes read from the source on a backup, or written to the target on a restore.",
            "format": "int64",
            "type": "integer"
          },
          "bytes_total": {
            "description": "How many bytes this target covers.",
            "format": "int64",
            "type": "integer"
          },
          "chunks_done": {
            "description": "How many chunks have been handled.",
            "format": "int64",
            "type": "integer"
          },
          "chunks_total": {
            "description": "How many chunks this target has.",
            "format": "int64",
            "type": "integer"
          },
          "delta": {
            "description": "True when only what changed since the previous backup is being read.",
            "type": "boolean"
          },
          "error": {
            "description": "Why this target failed. Absent unless it did.",
            "type": "string"
          },
          "eta_seconds": {
            "description": "Estimated seconds left on this target.",
            "format": "int64",
            "type": "integer"
          },
          "finished_at": {
            "description": "When work on this target finished. Absent until then.",
            "format": "date-time",
            "type": "string"
          },
          "key": {
            "description": "Identifies the disk or bucket. Job events about it carry the same value as `target`.",
            "type": "string"
          },
          "label": {
            "description": "The disk or bucket in words.",
            "type": "string"
          },
          "percent": {
            "description": "`bytes_done` plus `skipped_bytes`, as a share of `bytes_total`, from 0 to 100.",
            "type": "number"
          },
          "phase": {
            "description": "What the job is doing on it now.",
            "type": "string"
          },
          "segments_done": {
            "description": "How many data map segments have been handled.",
            "format": "int64",
            "type": "integer"
          },
          "segments_total": {
            "description": "How many data map segments this target has.",
            "format": "int64",
            "type": "integer"
          },
          "skipped_bytes": {
            "description": "Bytes the source declared empty, counted as covered without being transferred.",
            "format": "int64",
            "type": "integer"
          },
          "started_at": {
            "description": "When work on this target started.",
            "format": "date-time",
            "type": "string"
          },
          "stored_bps": {
            "description": "`stored_bytes` per second since this target started.",
            "format": "int64",
            "type": "integer"
          },
          "stored_bytes": {
            "description": "Repository traffic, in bytes. On a backup, what was actually written. On a restore, what was fetched.",
            "format": "int64",
            "type": "integer"
          },
          "throughput_bps": {
            "description": "`bytes_done` per second since this target started.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "TenantUsageView": {
        "description": "What one project holds in a repository.",
        "properties": {
          "computed_at": {
            "description": "When a sweep last measured `referenced_bytes` and `exclusive_bytes`. Absent when none has.",
            "format": "date-time",
            "type": "string"
          },
          "exclusive_bytes": {
            "description": "The bytes referenced by exactly one of the project's backups, which is what deleting backups one at a time would free. Absent until a sweep has computed it.",
            "format": "int64",
            "type": "integer"
          },
          "logical_allocated_bytes": {
            "description": "The written size of the protected disks and buckets, summed over the backups `point_count` counts.",
            "format": "int64",
            "type": "integer"
          },
          "logical_provisioned_bytes": {
            "description": "The provisioned size of the protected disks, summed over the same backups. Zero while `logical_allocated_bytes` is above zero means only buckets, which have no provisioned size, are backed up.",
            "format": "int64",
            "type": "integer"
          },
          "point_count": {
            "description": "How many of the project's backups are here, counting deleted ones not yet erased.",
            "type": "integer"
          },
          "project_id": {
            "description": "The project's id.",
            "type": "string"
          },
          "referenced_bytes": {
            "description": "The bytes of distinct blocks the project's backups here reference, deduplicated within the project, as of `computed_at`. Absent until a sweep has measured the project here.",
            "format": "int64",
            "type": "integer"
          },
          "resource_count": {
            "description": "How many distinct resources those backups protect.",
            "type": "integer"
          },
          "soft_deleted_bytes": {
            "description": "The part of `referenced_bytes` used only by backups hidden from the project: deleted and awaiting erasure, or expired and held by object lock. Absent when zero.",
            "format": "int64",
            "type": "integer"
          },
          "unavailable": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "`referenced_bytes` and `exclusive_bytes`, when absent, mapped to why. `sweep_pending`: no sweep has measured them yet. `gc_degraded`: the repository's sweeps are failing to complete. `rbd_only_no_manifests`: an rbd tier has no deduplicated footprint. Absent when both are present.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "TestReq": {
        "description": "A request to bring copies of the group up in an isolated network, check them and tear them down. The body may be left out.",
        "properties": {
          "bubble_policy_id": {
            "description": "The test policy to test under, from GET /v1/bubble-policies. Defaults to the one the group's registration names; with neither, the test is refused with `bubble_policy_required`.",
            "type": "string"
          },
          "reason": {
            "description": "Why, recorded with the run.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "TierEstimate": {
        "description": "How long restoring a backup from one copy would take, and how that was worked out.",
        "properties": {
          "available": {
            "description": "Whether a restore can read from this copy now. The instant tier is available where it is at this site, every disk of the backup is a Cinder volume, and this service can clone into the pool Cinder serves and hand the volume to the project.",
            "type": "boolean"
          },
          "basis": {
            "description": "How the estimate was reached. `rbd_clone_o1`: a clone, which takes seconds whatever the size. `chunk_count_x_measured_throughput`: the data to read divided by the restore speed measured at this site. `chunk_count_x_assumed_throughput`: the same with a conservative default speed, because none has been measured. `unavailable`: the repository copy cannot be estimated. The instant tier always reports `rbd_clone_o1`, available or not, so read `available` first.",
            "enum": [
              "rbd_clone_o1",
              "chunk_count_x_measured_throughput",
              "chunk_count_x_assumed_throughput",
              "unavailable"
            ],
            "type": "string"
          },
          "bytes": {
            "description": "How many bytes a restore from the repository would read. Absent for the instant tier and when unavailable.",
            "format": "int64",
            "type": "integer"
          },
          "estimated_restore_seconds": {
            "description": "The estimate, in seconds. 0 when the copy is not available.",
            "format": "int64",
            "type": "integer"
          },
          "reason": {
            "description": "Why this copy is not available, in words. Absent when it is. For the instant tier it says where the backup stands: waiting to be copied to the tier, being copied, stalled because the mover copying it stopped reporting (retried once its lease lapses), aged out of it, a copy that did not complete, or never copied at all.",
            "type": "string"
          },
          "repository_id": {
            "description": "The repository or tier that holds this copy. Absent when there is none.",
            "type": "string"
          },
          "tier": {
            "description": "`chunk`: the repository copy. `rbd`: the instant tier, which keeps recent backups as Ceph images.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "Tiers": {
        "description": "The repositories a policy writes to.",
        "properties": {
          "backup": {
            "allOf": [
              {
                "$ref": "#/components/schemas/BackupTier"
              }
            ],
            "description": "The repository every backup is written to."
          },
          "copy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CopyTier"
              }
            ],
            "description": "A second repository every backup is copied to, with its own retention. Absent when the policy has none."
          },
          "instant": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InstantTier"
              }
            ],
            "description": "The instant tier, an rbd tier that also keeps recent backups as Ceph images, which restores of new volumes and servers are cloned from where this site serves it. Absent when the policy has none."
          }
        },
        "type": "object"
      },
      "TiersReq": {
        "description": "Where a policy's backups are stored.",
        "properties": {
          "backup": {
            "allOf": [
              {
                "$ref": "#/components/schemas/BackupTierReq"
              }
            ],
            "description": "The repository every backup is written to. Required."
          },
          "copy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CopyTierReq"
              }
            ],
            "description": "Optional. Copies every backup to a second repository, with its own retention."
          },
          "instant": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InstantTierReq"
              }
            ],
            "description": "Optional. An instant tier: an rbd tier that also keeps recent backups as Ceph images. Where this site serves it, a restore of a new volume or server is cloned from it in seconds, and the policy's sentence says so; everything else reads the backup repository."
          }
        },
        "type": "object"
      },
      "TimingsView": {
        "description": "Time spent in each phase of a backup, in milliseconds. Each phase is summed over the workers that ran it in parallel, so the total can exceed the backup's wall-clock time.",
        "properties": {
          "t_ceph_read_ms": {
            "description": "Reading source data.",
            "format": "int64",
            "type": "integer"
          },
          "t_diff_ms": {
            "description": "Working out what changed since the previous backup.",
            "format": "int64",
            "type": "integer"
          },
          "t_hash_compress_ms": {
            "description": "Hashing, compressing and encrypting chunks.",
            "format": "int64",
            "type": "integer"
          },
          "t_repo_write_ms": {
            "description": "Writing to the repository.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "UnprotectedResourceView": {
        "description": "A resource no plan names.",
        "properties": {
          "name": {
            "description": "The resource's name. Absent when it has none.",
            "type": "string"
          },
          "resource_id": {
            "description": "The resource's id.",
            "type": "string"
          },
          "resource_kind": {
            "description": "The kind of resource.",
            "enum": [
              "volume",
              "instance"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "UpdatePolicyResp": {
        "description": "A policy after a change, as an operator sees it. With `apply_to_existing`, it also says what happened to the backups already taken.",
        "properties": {
          "applied": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ApplyResult"
              }
            ],
            "description": "What re-dating the existing backups did. Only with `apply_to_existing`."
          },
          "assigned_to": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PolicyAssignment"
              }
            ],
            "description": "The domains and projects the policy is assigned to. Operators only.",
            "x-operator-only": true
          },
          "created_at": {
            "description": "When the policy was created.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "Free text the operator wrote. Absent when empty.",
            "type": "string"
          },
          "id": {
            "description": "The policy's id.",
            "type": "string"
          },
          "mandatory": {
            "description": "True when a project cannot pause or delete protection under this policy.",
            "type": "boolean"
          },
          "manual": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ManualView"
              }
            ],
            "description": "How long a manual backup in a plan under this policy may be kept, from the vault's terms and the policy's retention. Absent when the vault allows no manual backups, or there is no vault."
          },
          "name": {
            "description": "The policy's name, unique across the cloud.",
            "type": "string"
          },
          "plan_count": {
            "description": "How many plans name the policy, deleted plans still inside their purge grace included. Operators only.",
            "type": "integer",
            "x-operator-only": true
          },
          "sentence": {
            "description": "What the policy promises, as a paragraph of English for people. The wording may change between releases.",
            "type": "string"
          },
          "spec": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PolicySpecView"
              }
            ],
            "description": "The policy's terms. Durations are integers in nanoseconds. Only operators are shown the storage tiers, the cron expression, the recovery site id and the test network policy."
          },
          "state": {
            "description": "`active`: new plans may name it. `deprecated`: plans under it keep running, and no new plan may name it. `retired`: it has no plans, and none may name it.",
            "enum": [
              "active",
              "deprecated",
              "retired"
            ],
            "type": "string"
          },
          "unsatisfied": {
            "description": "Why the policy cannot be honoured right now, one code each. Absent when it can. While any is listed, plans under the policy take no scheduled backups, and each one skipped is recorded as missed. `backup_repository_gone`, `instant_repository_gone`, `copy_repository_gone`: the repository is no longer registered. The `_not_a_` codes: it is no longer of the kind the tier needs. `retention_below_repository_floor`, `retention_above_repository_ceiling`: the retention no longer fits a locked repository's limits. `recovery_site_gone`: the recovery site is no longer registered. `recovery_site_cannot_read_any_copy`: no repository the policy writes to is reachable from it. `repository_unavailable`: a repository the policy writes to is not answering.",
            "items": {
              "enum": [
                "backup_repository_gone",
                "backup_repository_not_a_chunk_tier",
                "instant_repository_gone",
                "instant_repository_not_an_rbd_tier",
                "copy_repository_gone",
                "copy_repository_not_a_chunk_tier",
                "retention_below_repository_floor",
                "retention_above_repository_ceiling",
                "recovery_site_gone",
                "recovery_site_cannot_read_any_copy",
                "repository_unavailable"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "updated_at": {
            "description": "When the policy last changed, including its `state` and its `unsatisfied` codes.",
            "format": "date-time",
            "type": "string"
          },
          "vault": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VaultRefView"
              }
            ],
            "description": "The vault on the backup repository, where the backups are kept. Absent when that repository has no vault."
          },
          "visibility": {
            "description": "`public`: offered to every project. `assigned`: offered only to the domains and projects in `assigned_to`.",
            "enum": [
              "public",
              "assigned"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "UsagePerRepo": {
        "description": "A project's usage in one repository.",
        "properties": {
          "computed_at": {
            "description": "When the sweep that measured `stored_bytes` ran. Absent when none has.",
            "format": "date-time",
            "type": "string"
          },
          "kind": {
            "description": "The repository's kind.",
            "enum": [
              "chunk",
              "rbd"
            ],
            "type": "string"
          },
          "logical_allocated_bytes": {
            "description": "The written size of the protected disks and buckets, summed over the project's visible backups here.",
            "format": "int64",
            "type": "integer"
          },
          "logical_provisioned_bytes": {
            "description": "The provisioned size of the protected disks, summed the same way.",
            "format": "int64",
            "type": "integer"
          },
          "name": {
            "description": "The repository's name. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "point_count": {
            "description": "How many of the project's visible backups are here.",
            "type": "integer"
          },
          "repository_id": {
            "description": "The repository's id.",
            "type": "string"
          },
          "soft_deleted_bytes": {
            "description": "Bytes used here only by backups the project can no longer see. Absent when not measured. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "stored_bytes": {
            "description": "The project's deduplicated footprint in this repository, leaving out blocks used only by deleted backups. Absent, with `unavailable_reason`, when it has not been measured.",
            "format": "int64",
            "type": "integer"
          },
          "unavailable_reason": {
            "description": "Why `stored_bytes` is absent. `sweep_pending`: no sweep has measured the project here yet. `rbd_only_no_manifests`: an rbd tier keeps full copies and has no deduplicated footprint, so none ever will. `gc_degraded`, shown to operators only: sweeps of this repository are failing to complete. Absent when `stored_bytes` is present.",
            "enum": [
              "sweep_pending",
              "rbd_only_no_manifests",
              "gc_degraded"
            ],
            "type": "string"
          },
          "vault": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VaultRefView"
              }
            ],
            "description": "The vault that offers this repository, when there is one."
          }
        },
        "type": "object"
      },
      "UsageResponse": {
        "description": "A project's backup usage. What it protects and what its backups occupy, in total and per repository.",
        "properties": {
          "computed_at": {
            "description": "The oldest of the sweeps behind `stored_bytes`: the figure is at least this fresh, and may be older than `live_as_of` by as much as a sweep interval. Absent when no repository has been swept for this project.",
            "format": "date-time",
            "type": "string"
          },
          "live_as_of": {
            "description": "When `protected_bytes`, `provisioned_bytes`, `point_count` and `protected_resources` were read: this request, always, since they come from the live backup table rather than a sweep.",
            "format": "date-time",
            "type": "string"
          },
          "point_count": {
            "description": "How many backups `protected_bytes` sums over.",
            "type": "integer"
          },
          "project_id": {
            "description": "The project this describes.",
            "type": "string"
          },
          "protected_bytes": {
            "description": "The written size of each backup's disks and buckets, summed over the project's restorable backups: those committed or verified and not deleted. A disk with 10 GiB written, kept in seven backups, counts 70 GiB, so this measures what has been backed up, not the size of what is protected. A backup counts as soon as it is committed; broken, expired and deleted backups do not.",
            "format": "int64",
            "type": "integer"
          },
          "protected_resources": {
            "description": "How many distinct resources those backups protect.",
            "type": "integer"
          },
          "provisioned_bytes": {
            "description": "The provisioned size of the protected disks, summed over the same backups. When the project backs up only buckets, which have no provisioned size, it is 0 and listed in `unavailable`.",
            "format": "int64",
            "type": "integer"
          },
          "repositories": {
            "description": "The same figures for each repository that holds anything of the project's. Empty when no repository does.",
            "items": {
              "$ref": "#/components/schemas/UsagePerRepo"
            },
            "type": "array"
          },
          "soft_deleted_bytes": {
            "description": "Bytes used only by backups the project can no longer see: deleted and awaiting erasure, or expired and held by object lock. Not part of `stored_bytes`. Absent when zero. Operators only.",
            "format": "int64",
            "type": "integer",
            "x-operator-only": true
          },
          "stored_bytes": {
            "description": "The project's own deduplicated footprint in bytes, summed over the repositories: the distinct blocks its visible backups reference, measured within the project only, so other projects' data never changes it. Recomputed by periodic sweeps, so it can lag. This is the figure to bill on.",
            "format": "int64",
            "type": "integer"
          },
          "unavailable": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Each figure that could not be given, mapped to why. `stored_bytes`: `sweep_pending` when the project has backups but no sweep has measured any of them yet, or for an operator `gc_degraded` when sweeps are failing to complete. `provisioned_bytes`: `namespace_has_no_provisioned_size` when the project backs up only buckets. Absent when everything was measured.",
            "type": "object"
          }
        },
        "type": "object"
      },
      "VaultManual": {
        "description": "A vault's manual-backup terms as the operator wrote them.",
        "properties": {
          "allowed": {
            "description": "Whether manual backups may be taken in the vault.",
            "type": "boolean"
          },
          "max_keep": {
            "description": "The longest a manual backup may be kept, in nanoseconds, as written. Absent when not set.",
            "format": "int64",
            "type": "integer"
          },
          "max_per_day": {
            "description": "How many manual backups may be taken a day. Absent when there is no limit.",
            "type": "integer"
          },
          "until_deleted": {
            "description": "Whether keeping until deleted is offered, as written. A locked repository withdraws the offer. Absent when false.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "VaultManualReq": {
        "description": "The terms for manual backups in a vault. On a change, a field left out is kept.",
        "properties": {
          "allowed": {
            "description": "Whether manual backups may be taken here. Defaults to `true` when the block is sent on creation.",
            "type": "boolean"
          },
          "max_keep": {
            "description": "The longest a manual backup may be kept, as a duration such as `2160h`. Required while manual backups are allowed. It must be at least a day, or the repository's lock floor when that is longer. On a locked repository it must be at most the longest lock the repository and the cloud allow.",
            "type": "string"
          },
          "max_per_day": {
            "description": "How many manual backups may be taken a day: per plan, and per project in this vault for those outside plans. The day is the cloud's local calendar day. 0, the default, is no limit.",
            "type": "integer"
          },
          "until_deleted": {
            "description": "`true` lets a manual backup be kept until the project's users delete it. Refused on a locked repository.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "VaultManualView": {
        "description": "The terms a manual backup gets in a vault.",
        "properties": {
          "keep_at_least": {
            "description": "The shortest a manual backup is kept, in nanoseconds. A day, or the repository's lock floor when that is longer.",
            "format": "int64",
            "type": "integer"
          },
          "keep_at_most": {
            "description": "The longest a manual backup may be kept, in nanoseconds. The vault's `max_keep`, lowered to the repository's lock ceiling, and never below `keep_at_least`.",
            "format": "int64",
            "type": "integer"
          },
          "max_per_day": {
            "description": "How many manual backups may be taken a day. Absent when there is no limit.",
            "type": "integer"
          },
          "until_deleted": {
            "description": "True when a manual backup may be kept until deleted. Never on a locked repository. Absent when false.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "VaultRefView": {
        "description": "A reference to a vault.",
        "properties": {
          "id": {
            "description": "The vault's id.",
            "type": "string"
          },
          "immutable": {
            "description": "True when the vault's repository keeps backups under an object lock.",
            "type": "boolean"
          },
          "name": {
            "description": "The vault's name.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "VaultReq": {
        "description": "The body of a vault creation or change. On a change, a field left out is kept.",
        "properties": {
          "description": {
            "description": "Free text projects see. An empty string clears it.",
            "type": "string"
          },
          "manual": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VaultManualReq"
              }
            ],
            "description": "The terms for manual backups. Left out on creation, manual backups are not allowed in the vault."
          },
          "name": {
            "description": "The name projects see, unique across the cloud and at most 191 characters. Required on creation.",
            "type": "string"
          },
          "public": {
            "description": "`true` offers the vault to every project. `false`, the default, offers it only to the projects offered a policy that keeps backups in its repository.",
            "type": "boolean"
          },
          "repository_id": {
            "description": "The id of the chunk repository the vault presents. Required on creation. A repository has at most one vault. On a change it may only repeat the current one.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "VaultView": {
        "description": "A vault as the caller sees it. Operators are also shown its repository, whether it is public and its terms as written.",
        "properties": {
          "created_at": {
            "description": "When the vault was created.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "Free text the operator wrote. Absent when empty.",
            "type": "string"
          },
          "id": {
            "description": "The vault's id.",
            "type": "string"
          },
          "immutable": {
            "description": "True when the repository is locked, so nothing kept here can be erased before its date.",
            "type": "boolean"
          },
          "manual": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VaultManualView"
              }
            ],
            "description": "The terms a manual backup gets here, after the repository's lock is applied. Absent when manual backups are not allowed."
          },
          "name": {
            "description": "The vault's name.",
            "type": "string"
          },
          "public": {
            "description": "True when the vault is offered to every project. False when it is offered only to projects offered a policy that keeps backups in its repository. Operators only.",
            "type": "boolean",
            "x-operator-only": true
          },
          "repository_id": {
            "description": "The repository the vault presents. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "repository_name": {
            "description": "That repository's name. Operators only.",
            "type": "string",
            "x-operator-only": true
          },
          "sentence": {
            "description": "The vault's terms in one sentence of English for people. Absent when the vault allows no manual backups and its repository is not locked. The wording may change between releases.",
            "type": "string"
          },
          "terms": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VaultManual"
              }
            ],
            "description": "The manual-backup terms as the operator wrote them, before the repository's lock is applied. Operators only.",
            "x-operator-only": true
          },
          "updated_at": {
            "description": "When the vault last changed.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "VaultViewPage": {
        "description": "One page of a collection.",
        "properties": {
          "items": {
            "description": "The objects on this page, in the collection's order.",
            "items": {
              "$ref": "#/components/schemas/VaultView"
            },
            "type": "array"
          },
          "limit": {
            "description": "The page size that was applied, after any lowering to the maximum.",
            "type": "integer"
          },
          "next_marker": {
            "description": "Send this back as `marker` to read the next page. Absent on the last page.",
            "type": "string"
          },
          "total": {
            "description": "How many objects the whole collection holds, when the service counts it. Absent when it does not.",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "VerifyReq": {
        "description": "How often a policy's backups are checked.",
        "properties": {
          "every": {
            "description": "How often the newest backup of each protected resource is re-read and checked, as a duration. Required in this block.",
            "type": "string"
          }
        },
        "type": "object"
      },
      "VerifyView": {
        "description": "How often a policy's backups are verified.",
        "properties": {
          "every": {
            "description": "The interval, in nanoseconds, at which the newest backup of each plan is read back and checked.",
            "format": "int64",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "VersionInventoryView": {
        "description": "An inventory of the object versions in a versioned bucket, taken during a cleanup sweep.",
        "properties": {
          "beyond_ceiling": {
            "description": "Object versions locked for longer than the longest retention this cloud allows. Anything above zero means something other than the service set those locks.",
            "format": "int64",
            "type": "integer"
          },
          "delete_markers": {
            "description": "Delete markers left in the bucket.",
            "format": "int64",
            "type": "integer"
          },
          "foreign_objects": {
            "description": "Objects under the repository's prefix that the service did not write. Anything above zero means something else is writing to this storage.",
            "format": "int64",
            "type": "integer"
          },
          "noncurrent_bytes": {
            "description": "The total size of those versions, in bytes.",
            "format": "int64",
            "type": "integer"
          },
          "noncurrent_objects": {
            "description": "Old versions of objects that still exist, because their lock has not lapsed or they could not be deleted.",
            "format": "int64",
            "type": "integer"
          },
          "taken_at": {
            "description": "When the inventory was taken.",
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "VolumePlan": {
        "description": "The decisions for one volume in the backup.",
        "properties": {
          "availability_zone": {
            "description": "The availability zone here. Absent when unresolved or unrecorded.",
            "type": "string"
          },
          "source_volume_id": {
            "description": "The volume in the backup.",
            "type": "string"
          },
          "volume_type": {
            "description": "The volume type here. Absent when left to this cloud's default.",
            "type": "string"
          }
        },
        "type": "object"
      }
    },
    "securitySchemes": {
      "token": {
        "description": "A Keystone token: scoped to the project the call acts in, or system-scoped for operator calls.",
        "in": "header",
        "name": "X-Auth-Token",
        "type": "apiKey"
      }
    }
  },
  "info": {
    "description": "Every call is JSON over HTTPS. This section states what holds for every operation, so the\noperations below describe only what is particular to them.\n\n## Authentication and scope\n\nSend a Keystone token in `X-Auth-Token`. A project-scoped token acts in its own project: it\nsees and changes that project's plans, backups and restores and nothing else. Operator calls\nneed a system-scoped token (`system: all`); each operation names the access it requires.\n\nAn operator can act on one project with `?project_id=`. `?scope=tenant` shows an operator what\nthe project itself sees. It only narrows what an operator sees: a project-scoped token is always\nanswered as its project, whatever it asks for.\n\n## Terms\n\nA backup is called a point in paths and fields: `/v1/points`, `point_id`. The prose says backup.\nA project is the OpenStack project that owns plans, backups and restores.\n\n## Errors\n\nErrors are [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) problem documents, sent as\n`application/problem+json`. `code` is stable and safe to branch on; it is also the last segment of the\n`type` URN. `title` and `detail` are prose for people and may\nchange between releases. `request_id` is the id to quote when reporting a failure; the same\nvalue comes back in the `X-Request-Id` response header. Some problems carry a structured body\nof their own (`fields`, `refusals`, `coverage`, `quota`), named on the operations that return\nthem.\n\nEvery operation that needs a token can answer `401 unauthenticated`. Any operation can answer\n`500 internal_error` or `503 service_unavailable`; retry those with backoff. A\n`503 service_unavailable` caused by a service this one depends on not answering in time, such as\nKeystone while it validates the token, carries a `Retry-After` header: wait that many seconds\nbefore retrying.\n\n## Pagination\n\nCollections return `{items, limit, next_marker, total}`. `limit` defaults to 50 and is capped at\n500; a larger value is lowered, not refused. To read the next page, send the previous page's\n`next_marker` back as `marker`. `next_marker` is absent on the last page. A marker is opaque:\nbuild none yourself, and expect one to stop working across releases.\n\n## Values\n\n- Ids are opaque strings. OpenStack resources keep their OpenStack UUIDs.\n- Timestamps are RFC 3339 in UTC.\n- Durations are integers in nanoseconds unless a field says otherwise, for example\n  `rpo_target: 86400000000000` is one day.\n- Byte counts are integers in bytes.\n- A measurement the service could not take is absent, never zero. Where it matters the\n  response carries an `unavailable` map from the field name to the reason it is missing, so\n  \"not measured\" and \"measured as zero\" are told apart.\n- Fields marked `x-operator-only` are returned only to operators; a project-scoped token never\n  sees them.\n",
    "title": "Ferry API",
    "version": "0.16.0"
  },
  "openapi": "3.0.3",
  "paths": {
    "/v1/attachments": {
      "get": {
        "description": "Any member of the project the token is scoped to.",
        "operationId": "listAttachments",
        "parameters": [
          {
            "description": "Operator only: act on this project instead of the token's own.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only attachments of this backup.",
            "in": "query",
            "name": "point_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentListViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not attach backups in this project. Or `project_id` names a project it may not read."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List attachments",
        "tags": [
          "Attachments"
        ],
        "x-access": "member"
      },
      "post": {
        "description": "Presents one disk of a backup to a running server as a read-only volume, so files can be copied out with the server's own tools. `resource_id` picks the disk; the root disk otherwise. `expires_in` is how long it stays attached, as a duration such as `4h`. 24 hours when left out.\n\nAny member of the project the token is scoped to.",
        "operationId": "createAttachment",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "device": "/dev/vdc",
                "expires_in": "4h",
                "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                "resource_id": "7e9a1b2c-3d4e-4f5a-8b6c-9d0e1f2a3b4c",
                "target_instance_id": "f3a6c9e2-5b8d-4f1a-9c4e-7d0a3f6b9c2e"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateAttachmentReq"
              }
            }
          },
          "description": "The backup and the server to attach it to. `point_id` and `target_instance_id` are required. The answer is the attachment, already queued. It is ready when its `state` in GET /v1/attachments is `succeeded`.",
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "estimated_restore_seconds": 185,
                  "id": "d6f9b2e5-8a1c-4d4f-9b7e-0a3c6f9d2b5e",
                  "job_id": "5c8f1b4e-7a0d-4e3a-8c6f-9b2e5d8a1c4f",
                  "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "source_tier": "chunk",
                  "state": "pending",
                  "target_instance_id": "f3a6c9e2-5b8d-4f1a-9c4e-7d0a3f6b9c2e"
                },
                "schema": {
                  "$ref": "#/components/schemas/AttachmentView"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not attach backups in the backup's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The backup does not exist or is not visible to this token."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`point_not_restorable`: The backup's state is not `committed` or `verified`.\n\n`tier_unavailable`: `tier` is `rbd`: attachments read the repository copy. Or the repository copy cannot be read now."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `point_id` is not a UUID. `target_instance_id` is missing. `expires_in` is not a duration. `resource_id` names no disk of the backup. `tier` is not `chunk` or `rbd`. Or the backup is of a bucket, which cannot be attached."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Attach a backup to a server",
        "tags": [
          "Attachments"
        ],
        "x-access": "member"
      }
    },
    "/v1/attachments/{id}": {
      "delete": {
        "description": "Detaches the volume from the server and deletes it. An attachment still being set up is cancelled.\n\nAny member of the project the token is scoped to.",
        "operationId": "deleteAttachment",
        "parameters": [
          {
            "description": "The id of the attachment, as returned when it was created.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentListView"
                }
              }
            },
            "description": "Accepted"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not attach backups in the attachment's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no attachment has it, or it belongs to another project."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The attachment is already being detached."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Detach a backup",
        "tags": [
          "Attachments"
        ],
        "x-access": "member"
      }
    },
    "/v1/auth/handoff": {
      "post": {
        "description": "For a portal that holds the user's token. The code carries the caller's own session and nothing more. A browser redeems it once to be signed in without a password. It expires after a minute.\n\nAny valid token.",
        "operationId": "createHandoff",
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "code": "<one-time-code>",
                  "expires_at": "2026-10-02T09:15:05Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/HandoffView"
                }
              }
            },
            "description": "Created"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Create a one-time sign-in code",
        "tags": [
          "Authentication"
        ],
        "x-access": "token"
      }
    },
    "/v1/auth/handoff/redeem": {
      "post": {
        "description": "Answers the session the code carries. A code works once. A used or expired code is refused the same way. Attempts are rate limited.\n\nNo token is needed.",
        "operationId": "redeemHandoff",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "code": "<one-time-code>"
              },
              "schema": {
                "$ref": "#/components/schemas/RedeemHandoffReq"
              }
            }
          },
          "description": "The one-time code a portal created with POST /v1/auth/handoff.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "expires_at": "2026-10-02T21:14:05Z",
                  "project_id": "3f1c9a2e5b7d4e8f9a0b1c2d3e4f5a6b",
                  "project_name": "billing-prod",
                  "roles": [
                    "member",
                    "reader"
                  ],
                  "token": "<project-token>",
                  "user_name": "alice"
                },
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The code is unknown, malformed, already used or expired. All of these read the same."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `code` is missing."
          },
          "429": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Too Many Requests.\n\n`rate_limited`: More than 10 sign-in, scope or code attempts came from this address in the last minute."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "security": [],
        "summary": "Redeem a one-time sign-in code",
        "tags": [
          "Authentication"
        ],
        "x-access": "public"
      }
    },
    "/v1/auth/projects": {
      "get": {
        "description": "Answers `projects`, each with its id, name, domain and whether it is enabled. An unscoped token works.\n\nAny valid token.",
        "operationId": "listSessionProjects",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionProjectsView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List the projects a token can be scoped to",
        "tags": [
          "Authentication"
        ],
        "x-access": "token"
      }
    },
    "/v1/auth/session": {
      "get": {
        "description": "Who the token belongs to, its project, roles and expiry, and whether it may use the operator calls.\n\nAny valid token.",
        "operationId": "getSession",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "expires_at": "2026-10-02T21:14:05Z",
                  "grants": {
                    "dr:recover:unplanned": false,
                    "dr:release:override": false,
                    "dr:resync": false,
                    "dr:run:abort": false,
                    "dr:split_brain:acknowledge": false,
                    "protection_group:register_recovery": false,
                    "quota:write": false,
                    "repository:usage": false
                  },
                  "operator": false,
                  "project_id": "3f1c9a2e5b7d4e8f9a0b1c2d3e4f5a6b",
                  "project_name": "billing-prod",
                  "roles": [
                    "member",
                    "reader"
                  ],
                  "system_scope": false,
                  "user_id": "6c0f2d8e4a1b4c3d9e7f5a2b1c0d9e8f",
                  "user_name": "alice"
                },
                "schema": {
                  "$ref": "#/components/schemas/SessionView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show the caller's session",
        "tags": [
          "Authentication"
        ],
        "x-access": "token"
      },
      "post": {
        "description": "Exchanges a user name and password for a token. Naming no project gives an unscoped token, answered with the projects it can be scoped to and whether it can be scoped to the whole cloud. Attempts are rate limited.\n\nNo token is needed.",
        "operationId": "createSession",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "password": "<password>",
                "user": "alice",
                "user_domain": "Default"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateSessionReq"
              }
            }
          },
          "description": "The user's credentials and, optionally, the project to scope the token to. Without a project the answer is an unscoped token to exchange with POST /v1/auth/session/scope.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "expires_at": "2026-10-02T21:14:05Z",
                  "projects": [
                    {
                      "domain_id": "default",
                      "enabled": true,
                      "id": "3f1c9a2e5b7d4e8f9a0b1c2d3e4f5a6b",
                      "name": "billing-prod"
                    },
                    {
                      "domain_id": "default",
                      "enabled": true,
                      "id": "8d2e4f6a1b3c4d5e9f0a7b8c9d0e1f2a",
                      "name": "web-staging"
                    }
                  ],
                  "token": "<unscoped-token>",
                  "unscoped": true,
                  "user_name": "alice"
                },
                "schema": {
                  "$ref": "#/components/schemas/ScopedSession"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The identity service did not accept the credentials, the domain or the project. Every refusal reads the same, so an unknown user cannot be told from a wrong password."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `user` or `password` is missing."
          },
          "429": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Too Many Requests.\n\n`rate_limited`: More than 10 sign-in, scope or code attempts came from this address in the last minute."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "security": [],
        "summary": "Sign in",
        "tags": [
          "Authentication"
        ],
        "x-access": "public"
      }
    },
    "/v1/auth/session/scope": {
      "post": {
        "description": "Exchanges the caller's token, scoped or not, for one scoped to `project_id`. With `system`, the new token is scoped to the whole cloud instead, which operator calls need. Attempts are rate limited.\n\nAny valid token.",
        "operationId": "scopeSession",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "project_id": "3f1c9a2e5b7d4e8f9a0b1c2d3e4f5a6b"
              },
              "schema": {
                "$ref": "#/components/schemas/ScopeSessionReq"
              }
            }
          },
          "description": "The scope for the new token: one project by `project_id`, or the whole cloud with `system`. The token being exchanged travels in `X-Auth-Token`, not in the body.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "expires_at": "2026-10-02T21:14:05Z",
                  "project_id": "3f1c9a2e5b7d4e8f9a0b1c2d3e4f5a6b",
                  "project_name": "billing-prod",
                  "roles": [
                    "member",
                    "reader"
                  ],
                  "token": "<project-token>",
                  "user_name": "alice"
                },
                "schema": {
                  "$ref": "#/components/schemas/ScopedSession"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The identity service would not scope the token. The user holds no role on that project, or on the cloud itself for `system`, or the token is no longer valid."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: Neither `project_id` nor `system` is given, or both are."
          },
          "429": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Too Many Requests.\n\n`rate_limited`: More than 10 sign-in, scope or code attempts came from this address in the last minute."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Scope a session",
        "tags": [
          "Authentication"
        ],
        "x-access": "token"
      }
    },
    "/v1/bubble-policies": {
      "get": {
        "description": "The isolated networks a test can run in, as the operator has set them up. A test names one by its id, or runs under the one its group's registration names.\n\nAny member of the project the token is scoped to.",
        "operationId": "listBubblePolicies",
        "parameters": [
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BubblePolicyPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not run tests in its project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List test policies",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      },
      "post": {
        "description": "Sets up an isolated network a test can run in: the operator project it is built in, the address ranges it allocates from, whether its copies may send traffic out of it, the jump host health checks run from, the health check types allowed, the addresses it must prove it cannot reach, and how long a test under it may last. A registration names the policy its tests run under, and a test can name one itself.\n\nOperator only: needs a system-scoped token.",
        "operationId": "createBubblePolicy",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "isolation_canaries": [
                  "10.20.0.15:5432",
                  "203.0.113.10:443"
                ],
                "jump_host": true,
                "jump_host_flavor": "m1.small",
                "jump_host_image": "c0a8f1e2-3b4d-4c5e-9f60-7182a3b4c5d6",
                "max_duration": "2h",
                "name": "isolated-drill",
                "network_cidrs": [
                  "10.250.0.0/24"
                ],
                "permitted_checks": [
                  "tcp",
                  "http"
                ],
                "project_id": "9a8b7c6d5e4f40312a1b0c9d8e7f6a5b",
                "strict": true
              },
              "schema": {
                "$ref": "#/components/schemas/BubblePolicyReq"
              }
            }
          },
          "description": "The policy's settings. `name`, `project_id` and `network_cidrs` are required.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-03T09:00:00Z",
                  "id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
                  "isolation_canaries": [
                    "10.20.0.15:5432",
                    "203.0.113.10:443"
                  ],
                  "jump_host": true,
                  "jump_host_flavor": "m1.small",
                  "jump_host_image": "c0a8f1e2-3b4d-4c5e-9f60-7182a3b4c5d6",
                  "max_duration": 7200000000000,
                  "name": "isolated-drill",
                  "network_cidrs": [
                    "10.250.0.0/24"
                  ],
                  "permitted_checks": [
                    "tcp",
                    "http"
                  ],
                  "project_id": "9a8b7c6d5e4f40312a1b0c9d8e7f6a5b",
                  "strict": true,
                  "updated_at": "2026-10-03T09:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/BubblePolicy"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: A test policy with this name already exists. Or `project_id` names no project here, or one that is disabled."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `name`, `project_id` or `network_cidrs` is missing. A range is not in CIDR form or has host bits set. `max_duration` is not a duration from `10m` to `168h`. A permitted check is neither `tcp` nor `http`. An isolation canary is not `host:port` with a port from 1 to 65535, or canaries are given on a strict policy with no jump host. `fields` names each."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Create a test policy",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/bubble-policies/{id}": {
      "delete": {
        "description": "Removes a test policy no registration names and no test ran under. A run keeps naming the policy it ran under, so a policy any test ran under is kept; point registrations at another policy to stop using it.\n\nOperator only: needs a system-scoped token.",
        "operationId": "deleteBubblePolicy",
        "parameters": [
          {
            "description": "The test policy's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No test policy with this id exists."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`bubble_policy_in_use`: A protection group's registration names the policy, or a test ran under it."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Delete a test policy",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "operator"
      },
      "patch": {
        "description": "Changes the fields sent and keeps the rest; a list sent replaces the old one whole. The result is checked as on create, and the project is checked again only when `project_id` is sent.\n\nOperator only: needs a system-scoped token.",
        "operationId": "updateBubblePolicy",
        "parameters": [
          {
            "description": "The test policy's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "isolation_canaries": [
                  "10.20.0.15:5432"
                ],
                "max_duration": "4h"
              },
              "schema": {
                "$ref": "#/components/schemas/BubblePolicyReq"
              }
            }
          },
          "description": "The fields to change.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-03T09:00:00Z",
                  "id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
                  "isolation_canaries": [
                    "10.20.0.15:5432"
                  ],
                  "jump_host": true,
                  "jump_host_flavor": "m1.small",
                  "jump_host_image": "c0a8f1e2-3b4d-4c5e-9f60-7182a3b4c5d6",
                  "max_duration": 14400000000000,
                  "name": "isolated-drill",
                  "network_cidrs": [
                    "10.250.0.0/24"
                  ],
                  "permitted_checks": [
                    "tcp",
                    "http"
                  ],
                  "project_id": "9a8b7c6d5e4f40312a1b0c9d8e7f6a5b",
                  "strict": true,
                  "updated_at": "2026-10-03T11:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/BubblePolicy"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No test policy with this id exists."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: Another test policy already has the new name. Or a new `project_id` names no project here, or one that is disabled."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: The policy after the change breaks a rule of creation. `fields` names each field."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Change a test policy",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/capabilities": {
      "get": {
        "description": "Whether the project has a protection policy to choose, whether backups can be encrypted, which kinds of resource can be protected, whether a protection group can be recovered or tested, and whether immutable backups are offered. A form can use it to stop offering what plan creation or a disaster recovery request would refuse.\n\nAny member of the project the token is scoped to.",
        "operationId": "getCapabilities",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "dr": true,
                  "dr_reason": "Warm, cold and hot groups recover here, planned and unplanned, test in a bubble, release, fail back to their originals, resync and retire. Hot groups recover from volumes mirrored in pool cinder-ceph with charmed. A test at a group's home after a failover does not run in this release.",
                  "encryption": true,
                  "immutability": true,
                  "policies": 3,
                  "protectable_kinds": [
                    "volume",
                    "instance",
                    "bucket"
                  ],
                  "protection": true
                },
                "schema": {
                  "$ref": "#/components/schemas/CapabilitiesResponse"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow creating plans in its project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show what this cloud offers",
        "tags": [
          "Resources"
        ],
        "x-access": "member"
      }
    },
    "/v1/coverage": {
      "get": {
        "description": "Per project, with totals: paused or unsatisfied plans, plans on a deprecated policy or on none, and missed backups. Also backups not yet copied or not immutably kept, backups whose point object is still being completed, unprotected resources and quota problems. `policies_below_minimum` lists the policies weaker than the cloud's minimum.\n\nOperator only: needs a system-scoped token.",
        "operationId": "getCoverage",
        "parameters": [
          {
            "description": "Only this project. `unprotected_resources` is listed only when this is set, because finding them means inspecting the project's resources.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only the projects of this Keystone domain.",
            "in": "query",
            "name": "domain_id",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "generated_at": "2026-10-02T10:30:00Z",
                  "minimum": {
                    "keep_all": 604800000000000,
                    "requires_lock": true
                  },
                  "projects": [
                    {
                      "domain_id": "default",
                      "missed_ticks": [
                        {
                          "count": 2,
                          "name": "web-tier",
                          "plan_id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60"
                        }
                      ],
                      "orphaned_points": [
                        {
                          "committed_at": "2026-09-09T13:19:21Z",
                          "point_id": "af690713-1704-48f1-bbbb-938f9adf7b79",
                          "resource_id": "3c2b1a0f-9e8d-4c7b-a6f5-e4d3c2b1a0f9",
                          "resource_kind": "volume"
                        }
                      ],
                      "plans": 4,
                      "plans_paused": [
                        {
                          "name": "legacy-db",
                          "plan_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
                          "policy_id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                          "policy_name": "standard",
                          "since": "2026-09-28T16:02:00Z"
                        }
                      ],
                      "plans_unsatisfied": [
                        {
                          "detail": "repository_unavailable",
                          "name": "web-tier",
                          "plan_id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                          "policy_id": "c8f2d3e4-5a6b-4c7d-9e8f-0a1b2c3d4e5f",
                          "policy_name": "gold"
                        }
                      ],
                      "point_objects_owed": [
                        {
                          "attempts": 3,
                          "last_error": "open repository backups: dial tcp 10.0.0.9:443: i/o timeout",
                          "owed_since": "2026-10-03T20:36:59Z",
                          "plan_id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                          "point_id": "7d2e9c41-5b3a-4f8e-a1c6-2e8f0b9d4a73",
                          "resource_id": "0b7c6d5e-4f3a-4b2c-9d1e-0f9a8b7c6d5e"
                        }
                      ],
                      "points_not_immutably_protected": [
                        {
                          "count": 31,
                          "name": "legacy-db",
                          "plan_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d"
                        }
                      ],
                      "project_id": "6f1e2d3c4b5a49788796a5b4c3d2e1f0",
                      "projects_over_quota": [
                        {
                          "dimension": "stored_bytes",
                          "domain_id": "default",
                          "in_use": 1209462790553,
                          "limit": 1099511627776,
                          "next_release_at": "2026-10-05T00:00:00Z",
                          "project_id": "6f1e2d3c4b5a49788796a5b4c3d2e1f0",
                          "reclaimable": true
                        }
                      ],
                      "unprotected_resources": [
                        {
                          "name": "scratch-data",
                          "resource_id": "3c2b1a0f-9e8d-4c7b-a6f5-e4d3c2b1a0f9",
                          "resource_kind": "volume"
                        }
                      ]
                    }
                  ],
                  "totals": {
                    "missed_ticks": 2,
                    "orphaned_points": 1,
                    "plans": 4,
                    "plans_on_deprecated_policy": 0,
                    "plans_paused": 1,
                    "plans_unsatisfied": 1,
                    "plans_without_policy": 0,
                    "point_objects_owed": 1,
                    "points_awaiting_copy": 0,
                    "points_not_immutably_protected": 31,
                    "policies_below_minimum": 0,
                    "projects_over_quota": 1,
                    "quotas_below_locked_footprint": 0,
                    "retired_under_mandatory": 0,
                    "unprotected_resources": 1
                  },
                  "window": 604800000000000
                },
                "schema": {
                  "$ref": "#/components/schemas/CoverageResponse"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not an operator's."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show what is not protected",
        "tags": [
          "Coverage"
        ],
        "x-access": "operator"
      }
    },
    "/v1/flr-sessions": {
      "get": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "listFileSessions",
        "parameters": [
          {
            "description": "Operator only: act on this project instead of the token's own.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlrSessionViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: `project_id` names another project and the token may not read other projects."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List open browse sessions",
        "tags": [
          "File-level restore"
        ],
        "x-access": "reader"
      },
      "post": {
        "description": "Opens a read-only session over a backup's disks, without restoring it. The answer lists the filesystems found on them, and the operating system is found and mounted straight away when there is one. ext4, XFS, NTFS and LVM volumes can be read. The session ends at its deadline, or sooner when left idle.\n\nAny reader of the project the token is scoped to.",
        "operationId": "openFileSession",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "point_id": "0b6f3c1e-8a42-4d7e-9c15-2f7a9e4b6d03"
              },
              "schema": {
                "$ref": "#/components/schemas/OpenFLRSessionReq"
              }
            }
          },
          "description": "The backup to browse. Only `point_id` has any effect.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-02T09:02:17Z",
                  "deadline": "2026-10-02T11:02:17Z",
                  "id": "5e2a9c71-3d4b-4f86-a0c2-7b19e6d8f453",
                  "point_id": "0b6f3c1e-8a42-4d7e-9c15-2f7a9e4b6d03",
                  "session": {
                    "deadline": "2026-10-02T11:02:17Z",
                    "disks": [
                      {
                        "fetched_bytes": 12582912,
                        "resource_id": "9a7c5e3b-1d2f-4a6b-8c0e-3f5a7b9c1d2e",
                        "role": "root",
                        "volume_bytes": 21474836480
                      }
                    ],
                    "filesystems": [
                      {
                        "device": "/dev/sda1",
                        "mountable": true,
                        "type": "ext4"
                      },
                      {
                        "device": "/dev/sda2",
                        "mountable": false,
                        "reason": "swap holds no files to restore",
                        "type": "swap"
                      }
                    ],
                    "mounts": [
                      {
                        "device": "/dev/sda1",
                        "mount_point": "/"
                      }
                    ]
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/FlrSessionView"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow browsing backups in that project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No backup has that id in the token's project, or it has been deleted."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The backup is still being written, is `broken` or has been deleted, so it has nothing consistent to read."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `point_id` is missing.\n\n`flr_no_manifests`: The backup holds no disk image, as with a bucket backup."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`flr_unavailable`: No worker at this site can browse right now. None is deployed with browsing, none is reachable, or each one is at its session limit or refused to open the backup."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Browse a backup's files",
        "tags": [
          "File-level restore"
        ],
        "x-access": "reader"
      }
    },
    "/v1/flr-sessions/{id}": {
      "delete": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "closeFileSession",
        "parameters": [
          {
            "description": "The browse session's id, as answered when it was opened.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow browsing backups in that project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No session has that id in the token's project."
          },
          "410": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Gone.\n\n`gone`: The session was already closed or is past its deadline."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Close a browse session",
        "tags": [
          "File-level restore"
        ],
        "x-access": "reader"
      },
      "get": {
        "description": "The filesystems found on the backup, which of them are mounted and where, any LVM volumes, and when the session ends.\n\nAny reader of the project the token is scoped to.",
        "operationId": "getFileSession",
        "parameters": [
          {
            "description": "The browse session's id, as answered when it was opened.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "deadline": "2026-10-02T11:02:17Z",
                  "filesystems": [
                    {
                      "device": "/dev/sda1",
                      "mountable": true,
                      "type": "ext4"
                    },
                    {
                      "device": "/dev/vg0/data",
                      "mountable": true,
                      "type": "xfs"
                    }
                  ],
                  "lvm": {
                    "logical_volumes": [
                      "/dev/vg0/data"
                    ],
                    "physical_volumes": [
                      "/dev/sdb"
                    ],
                    "volume_groups": [
                      "vg0"
                    ]
                  },
                  "mounts": [
                    {
                      "device": "/dev/sda1",
                      "mount_point": "/",
                      "type": "ext4"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/SessionDetail"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow browsing backups in that project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No session has that id in the token's project."
          },
          "410": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Gone.\n\n`gone`: The session was closed or is past its deadline. Open another."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`flr_unavailable`: The worker holding the session did not answer."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a browse session",
        "tags": [
          "File-level restore"
        ],
        "x-access": "reader"
      }
    },
    "/v1/flr-sessions/{id}/download": {
      "get": {
        "description": "Streams one file. A `Range` header, or `offset` and `length`, fetch part of it, so an interrupted download can resume where it stopped.\n\nAny reader of the project the token is scoped to.",
        "operationId": "downloadFile",
        "parameters": [
          {
            "description": "The browse session's id, as answered when it was opened.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The absolute path of the file. A directory is refused with 400.",
            "in": "query",
            "name": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Start this many bytes into the file. With `offset` or `length` the answer is 206 with a `Content-Range` header. An offset past the end is refused with 416.",
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Return at most this many bytes. 0, or more than is left, means to the end of the file. A single-range `Range` header works the same way when neither parameter is given.",
            "in": "query",
            "name": "length",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/octet-stream": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow browsing backups in that project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No session has that id in the token's project."
          },
          "410": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Gone.\n\n`gone`: The session was closed or is past its deadline. Open another."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`flr_unavailable`: The worker holding the session did not answer."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Download a file",
        "tags": [
          "File-level restore"
        ],
        "x-access": "reader"
      }
    },
    "/v1/flr-sessions/{id}/mount": {
      "post": {
        "description": "Mounts the filesystem on `device` at `mount_point`, `/` when left out. With `inspect`, finds and mounts the operating system instead. Answers with everything now mounted.\n\nAny reader of the project the token is scoped to.",
        "operationId": "mountFileSession",
        "parameters": [
          {
            "description": "The browse session's id, as answered when it was opened.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "device": "/dev/vg0/data",
                "mount_point": "/srv"
              },
              "schema": {
                "$ref": "#/components/schemas/MountRequest"
              }
            }
          },
          "description": "The filesystem to mount and where, or `inspect` to mount the operating system. A device that is not on the disks or not mountable, or an inspection that finds no operating system, is refused with 400.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "mounts": [
                    {
                      "device": "/dev/sda1",
                      "mount_point": "/",
                      "type": "ext4"
                    },
                    {
                      "device": "/dev/vg0/data",
                      "mount_point": "/srv",
                      "type": "xfs"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/MountResult"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow browsing backups in that project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No session has that id in the token's project."
          },
          "410": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Gone.\n\n`gone`: The session was closed or is past its deadline. Open another."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`flr_unavailable`: The worker holding the session did not answer."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Mount a filesystem",
        "tags": [
          "File-level restore"
        ],
        "x-access": "reader"
      }
    },
    "/v1/flr-sessions/{id}/stat": {
      "get": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "statFileSession",
        "parameters": [
          {
            "description": "The browse session's id, as answered when it was opened.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The absolute path to describe; `/` when left out. A symbolic link is described itself, not followed.",
            "in": "query",
            "name": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "gid": 0,
                  "mode": 420,
                  "mtime": "2026-09-28T14:03:51Z",
                  "name": "app.conf",
                  "path": "/etc/app/app.conf",
                  "size": 2214,
                  "type": "file",
                  "uid": 0
                },
                "schema": {
                  "$ref": "#/components/schemas/Entry"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow browsing backups in that project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No session has that id in the token's project."
          },
          "410": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Gone.\n\n`gone`: The session was closed or is past its deadline. Open another."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`flr_unavailable`: The worker holding the session did not answer."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a file's details",
        "tags": [
          "File-level restore"
        ],
        "x-access": "reader"
      }
    },
    "/v1/flr-sessions/{id}/tree": {
      "get": {
        "description": "Answers the directory's `path` and its `entries`, each shaped like the answer of GET /v1/flr-sessions/{id}/stat.\n\nAny reader of the project the token is scoped to.",
        "operationId": "browseFileSession",
        "parameters": [
          {
            "description": "The browse session's id, as answered when it was opened.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The absolute path of the directory to list; `/` when left out. Nothing can be listed until a filesystem is mounted. A directory holding more than 50,000 entries cannot be listed.",
            "in": "query",
            "name": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Listing"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow browsing backups in that project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No session has that id in the token's project."
          },
          "410": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Gone.\n\n`gone`: The session was closed or is past its deadline. Open another."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`flr_unavailable`: The worker holding the session did not answer."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List a directory",
        "tags": [
          "File-level restore"
        ],
        "x-access": "reader"
      }
    },
    "/v1/jobs": {
      "get": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "listJobs",
        "parameters": [
          {
            "description": "Operator only: act on this project instead of the token's own.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only jobs of this plan.",
            "in": "query",
            "name": "plan_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only jobs for this backup.",
            "in": "query",
            "name": "point_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only jobs in this state. Repeat for several. `pending` waits to be claimed, including after a failed attempt that will be retried. `claimed` and `running` are being worked on. `succeeded` finished. `dead_letter` failed for good. `cancelled` was stopped.",
            "in": "query",
            "name": "state",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only jobs of this kind, such as `backup`, `restore`, `verify`, `expire` or `copy`. Repeat for several. A kind that does not exist matches nothing.",
            "in": "query",
            "name": "kind",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only jobs created at or after this RFC 3339 time.",
            "in": "query",
            "name": "since",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only jobs whose kind, error code or id contains this text.",
            "in": "query",
            "name": "q",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The field to sort by: `created_at`, `started_at`, `finished_at`, `state`, `kind` or `attempts`.",
            "in": "query",
            "name": "sort",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`asc`, the default, or `desc`. Used with `sort`.",
            "in": "query",
            "name": "order",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` adds `total`, the number of jobs matching the filter.",
            "in": "query",
            "name": "total",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `point:list` permission. Or `project_id` names another project and the token may not read other projects."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List jobs",
        "tags": [
          "Jobs"
        ],
        "x-access": "reader"
      }
    },
    "/v1/jobs/{id}": {
      "get": {
        "description": "Its state, progress and events, and for a backup how long each phase took.\n\nAny reader of the project the token is scoped to.",
        "operationId": "getJob",
        "parameters": [
          {
            "description": "The job's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "attempts": 1,
                  "created_at": "2026-10-02T02:00:00Z",
                  "domain_id": "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a",
                  "events": [
                    {
                      "at": "2026-10-02T02:07:14Z",
                      "code": "disk_started",
                      "level": "info",
                      "message": "backing up vda",
                      "seq": 1,
                      "target": "d2f4a6c8-e0b2-4d4f-a6c8-e0b2d4f6a8c0"
                    },
                    {
                      "at": "2026-10-02T02:07:18Z",
                      "code": "disk_incremental",
                      "level": "info",
                      "message": "vda is incremental \u00b7 6.0 GiB changed of 100 GiB",
                      "seq": 2,
                      "target": "d2f4a6c8-e0b2-4d4f-a6c8-e0b2d4f6a8c0"
                    },
                    {
                      "at": "2026-10-02T02:12:31Z",
                      "code": "disk_finished",
                      "level": "ok",
                      "message": "vda backed up \u00b7 6.0 GiB read in 5m0s (20.5 MiB/s) \u00b7 2.0 GiB written to the repository (6.8 MiB/s)",
                      "seq": 3,
                      "target": "d2f4a6c8-e0b2-4d4f-a6c8-e0b2d4f6a8c0"
                    }
                  ],
                  "finished_at": "2026-10-02T02:12:40Z",
                  "id": "7e5d3c1b-9a8f-4e6d-b2c4-a1f3e5d7c9b0",
                  "kind": "backup",
                  "max_attempts": 5,
                  "next_attempt_at": "2026-10-02T02:00:00Z",
                  "plan_id": "4c6e8a0b-2d4f-4a6c-8e0b-2d4f6a8c0e2b",
                  "point_id": "0f2e4d6c-8b1a-4c3e-9d5f-7a9b1c3d5e7f",
                  "progress": {
                    "bytes_done": 6442450944,
                    "bytes_total": 107374182400,
                    "chunks_done": 1536,
                    "chunks_total": 1536,
                    "eta_seconds": 0,
                    "percent": 100,
                    "phase": "commit",
                    "skipped_bytes": 100931731456,
                    "stored_bps": 7158278,
                    "stored_bytes": 2147483648,
                    "throughput_bps": 21474836
                  },
                  "project_id": "9a7c3e1f5b2d4a6c8e0f1a3b5c7d9e2f",
                  "repo_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60",
                  "scheduled_for": "2026-10-02T02:00:00Z",
                  "site_id": "b1d3f5a7-c9e2-4b4d-8f6a-0c2e4a6b8d0f",
                  "started_at": "2026-10-02T02:07:12Z",
                  "state": "succeeded",
                  "timings": {
                    "t_ceph_read_ms": 188400,
                    "t_diff_ms": 4210,
                    "t_hash_compress_ms": 61230,
                    "t_repo_write_ms": 47110
                  },
                  "updated_at": "2026-10-02T02:12:40Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/JobView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `point:list` permission in the job's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no job has it. Or the job belongs to a project the token may not read."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a job",
        "tags": [
          "Jobs"
        ],
        "x-access": "reader"
      }
    },
    "/v1/jobs/{id}/cancel": {
      "post": {
        "description": "Stops a job that has not finished. Nothing is rolled back: a backup cut short is never committed, and is cleaned up on its own.\n\nAny member of the project the token is scoped to.",
        "operationId": "cancelJob",
        "parameters": [
          {
            "description": "The job's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "attempts": 0,
                  "created_at": "2026-10-02T09:14:02Z",
                  "domain_id": "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a",
                  "finished_at": "2026-10-02T09:15:47Z",
                  "id": "a3c5e7f9-1b2d-4f6a-8c0e-2f4a6c8e0b1d",
                  "kind": "restore",
                  "max_attempts": 5,
                  "next_attempt_at": "2026-10-02T09:14:02Z",
                  "point_id": "0f2e4d6c-8b1a-4c3e-9d5f-7a9b1c3d5e7f",
                  "progress": {
                    "bytes_done": 0,
                    "bytes_total": 0,
                    "chunks_done": 0,
                    "chunks_total": 0,
                    "eta_seconds": 0,
                    "percent": 0,
                    "phase": "",
                    "skipped_bytes": 0,
                    "stored_bps": 0,
                    "stored_bytes": 0,
                    "throughput_bps": 0
                  },
                  "project_id": "9a7c3e1f5b2d4a6c8e0f1a3b5c7d9e2f",
                  "repo_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60",
                  "site_id": "b1d3f5a7-c9e2-4b4d-8f6a-0c2e4a6b8d0f",
                  "state": "cancelled",
                  "updated_at": "2026-10-02T09:15:47Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/JobView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `point:delete` permission in the job's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no job has it. Or the job belongs to a project the token may not read."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The job has already finished, as `succeeded`, `dead_letter` or `cancelled`."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Cancel a job",
        "tags": [
          "Jobs"
        ],
        "x-access": "member"
      }
    },
    "/v1/legal-holds": {
      "get": {
        "description": "Operator only: needs a system-scoped token.",
        "operationId": "listLegalHolds",
        "parameters": [
          {
            "description": "Only holds whose `scope` is this: `point`, `plan` or `project`.",
            "in": "query",
            "name": "hold_scope",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only holds on this backup, plan or project.",
            "in": "query",
            "name": "target_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only holds in this project.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`false` includes lifted holds. By default only active holds are listed.",
            "in": "query",
            "name": "active",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegalHoldViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `legal_hold:write`."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List legal holds",
        "tags": [
          "Legal holds"
        ],
        "x-access": "operator"
      },
      "post": {
        "description": "Keeps one backup, or every backup of a plan or a project, past its expiry date, and stops it being deleted until the hold is lifted. `scope` is `point`, `plan` or `project`, and `target_id` names what is held. `reason` is required.\n\nOperator only: needs a system-scoped token.",
        "operationId": "placeLegalHold",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "reason": "Litigation hold, case 2026-41",
                "scope": "plan",
                "target_id": "9b4d6f8a-0c2e-4a1b-8d3f-5e7a9c1b3d5f"
              },
              "schema": {
                "$ref": "#/components/schemas/PlaceLegalHoldReq"
              }
            }
          },
          "description": "What to hold, and why.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "active": true,
                  "id": "5d7f9b1d-3e5a-4c7e-9f1b-3d5e7a9c1b2d",
                  "placed_at": "2026-10-02T10:15:00Z",
                  "placed_by": "4b6d8f0a2c4e6a8b0d2f4a6c8e0b2d4f",
                  "project_id": "8c2e6b1f4a3d4e5f9a0b1c2d3e4f5a6b",
                  "reason": "Litigation hold, case 2026-41",
                  "scope": "plan",
                  "target_id": "9b4d6f8a-0c2e-4a1b-8d3f-5e7a9c1b3d5f"
                },
                "schema": {
                  "$ref": "#/components/schemas/LegalHoldView"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `legal_hold:write`."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: `scope` is `point` or `plan`, and no backup or plan has `target_id`."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `scope` is not `point`, `plan` or `project`. `target_id` or `reason` is missing. `target_id` is not a UUID for `scope` `point` or `plan`."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Place a legal hold",
        "tags": [
          "Legal holds"
        ],
        "x-access": "operator"
      }
    },
    "/v1/legal-holds/{id}": {
      "delete": {
        "description": "The backups it held go back to their own expiry dates.\n\nOperator only: needs a system-scoped token.",
        "operationId": "liftLegalHold",
        "parameters": [
          {
            "description": "The legal hold's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "active": false,
                  "id": "5d7f9b1d-3e5a-4c7e-9f1b-3d5e7a9c1b2d",
                  "lifted_at": "2026-12-14T08:40:00Z",
                  "lifted_by": "4b6d8f0a2c4e6a8b0d2f4a6c8e0b2d4f",
                  "placed_at": "2026-10-02T10:15:00Z",
                  "placed_by": "4b6d8f0a2c4e6a8b0d2f4a6c8e0b2d4f",
                  "project_id": "8c2e6b1f4a3d4e5f9a0b1c2d3e4f5a6b",
                  "reason": "Litigation hold, case 2026-41",
                  "scope": "plan",
                  "target_id": "9b4d6f8a-0c2e-4a1b-8d3f-5e7a9c1b3d5f"
                },
                "schema": {
                  "$ref": "#/components/schemas/LegalHoldView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `legal_hold:write`."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no legal hold has it."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The hold has already been lifted."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Lift a legal hold",
        "tags": [
          "Legal holds"
        ],
        "x-access": "operator"
      }
    },
    "/v1/movers": {
      "get": {
        "description": "The data workers at this site that carry out backups and restores, with their version, capacity, running jobs, last heartbeat and whether they are alive. A worker that cannot reach a repository is sent no work for it.\n\nOperator only: needs a system-scoped token.",
        "operationId": "listWorkers",
        "parameters": [
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MoverViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `repository:usage`."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List backup workers",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      }
    },
    "/v1/openapi.json": {
      "get": {
        "description": "This document. `title` overrides the name it carries.\n\nNo token is needed.",
        "operationId": "getOpenAPI",
        "parameters": [
          {
            "description": "The title to give the document. The service's own name when left out.",
            "in": "query",
            "name": "title",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {},
                  "type": "object"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "security": [],
        "summary": "Describe this API",
        "tags": [
          "OpenAPI document"
        ],
        "x-access": "public"
      }
    },
    "/v1/plans": {
      "get": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "listPlans",
        "parameters": [
          {
            "description": "Operator only: list this project's plans instead of the token's own.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`sla` attaches each plan's measured protection as `sla`, the same object GET /v1/plans/{id}/sla returns. No other value is accepted.",
            "in": "query",
            "name": "include",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Operator only: `true` lists the deleted plans that have not been purged yet, instead of the live ones.",
            "in": "query",
            "name": "deleted",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "Only plans of this kind, one of `volume`, `instance` or `bucket`.",
            "in": "query",
            "name": "resource_kind",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "items": [
                    {
                      "consistency": "filesystem",
                      "coverage": {
                        "scope": "full"
                      },
                      "created_at": "2026-09-12T09:41:12Z",
                      "domain_id": "default",
                      "enabled": true,
                      "encryption": "barbican",
                      "id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                      "last_run_at": "2026-10-02T02:00:00Z",
                      "name": "web-tier",
                      "next_run_at": "2026-10-03T02:00:00Z",
                      "policy": {
                        "created_at": "2026-09-01T08:00:00Z",
                        "id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                        "mandatory": false,
                        "name": "standard",
                        "sentence": "Every day at 02:00. Each backup is kept for 7 days, one a month for 12 months.",
                        "spec": {
                          "keep": {
                            "all": 604800000000000,
                            "anchors": {
                              "month": "first",
                              "week": 0,
                              "year": 1
                            },
                            "monthly": 12
                          },
                          "schedule": {
                            "at": "02:00",
                            "every": 86400000000000
                          }
                        },
                        "state": "active",
                        "updated_at": "2026-09-01T08:00:00Z",
                        "visibility": "public"
                      },
                      "policy_id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                      "project_id": "6f1e2d3c4b5a49788796a5b4c3d2e1f0",
                      "resource_ids": [
                        "e3a1b2c4-5d6e-4f70-8a9b-0c1d2e3f4a5b"
                      ],
                      "resource_kind": "instance",
                      "updated_at": "2026-09-12T09:41:12Z"
                    }
                  ],
                  "limit": 50
                },
                "schema": {
                  "$ref": "#/components/schemas/PlanViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow listing plans. Or `project_id` names a project this token may not read. Or `deleted=true` was asked for without operator rights."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List plans",
        "tags": [
          "Plans"
        ],
        "x-access": "reader"
      },
      "post": {
        "description": "Protects servers, volumes or a bucket under a protection policy. The policy sets the schedule, the retention and where backups are kept. The plan names what is protected and how consistent each backup is. A resource the service can protect only in part is refused with an account of what would and would not be covered.\n\nAny member of the project the token is scoped to.",
        "operationId": "createPlan",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "consistency": "filesystem",
                "encryption": "barbican",
                "excluded_disks": [
                  {
                    "resource_id": "0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d"
                  }
                ],
                "labels": {
                  "env": "prod",
                  "team": "web"
                },
                "name": "web-tier",
                "policy_id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                "resource_ids": [
                  "e3a1b2c4-5d6e-4f70-8a9b-0c1d2e3f4a5b"
                ],
                "resource_kind": "instance"
              },
              "schema": {
                "$ref": "#/components/schemas/CreatePlanReq"
              }
            }
          },
          "description": "The plan to create. `name`, `resource_kind`, `resource_ids` and `policy_id` are required. The service inspects every named resource before it creates anything. When some disks cannot be protected, the call is refused with `partial_coverage_unaccepted` and an account of what would and would not be protected. Repeat it with `partial_coverage` to accept that account.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "consistency": "filesystem",
                  "coverage": {
                    "accepted_at": "2026-10-02T09:41:12Z",
                    "accepted_by": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                    "digest": "sha256:3b8e5f1a9c2d4e6f8a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f",
                    "excluded": [
                      {
                        "detail": "left out of this plan by its owner",
                        "effect": "data_loss",
                        "kind": "volume",
                        "locator": "/dev/vdc",
                        "name": "volume-0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d",
                        "reason": "excluded_by_owner",
                        "resource_id": "0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d",
                        "resource_kind": "instance",
                        "size_bytes": 4398046511104
                      }
                    ],
                    "scope": "partial"
                  },
                  "created_at": "2026-10-02T09:41:12Z",
                  "domain_id": "default",
                  "enabled": true,
                  "encryption": "barbican",
                  "excluded_disks": [
                    {
                      "resource_id": "0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d"
                    }
                  ],
                  "id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                  "labels": {
                    "env": "prod",
                    "team": "web"
                  },
                  "missed_ticks": 0,
                  "name": "web-tier",
                  "next_run_at": "2026-10-03T02:00:00Z",
                  "policy": {
                    "created_at": "2026-09-01T08:00:00Z",
                    "description": "Nightly backups kept for a year.",
                    "id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                    "mandatory": false,
                    "manual": {
                      "keep_at_least": 604800000000000,
                      "keep_at_most": 7776000000000000,
                      "max_per_day": 3
                    },
                    "name": "standard",
                    "sentence": "Every day at 02:00. Each backup is kept for 7 days, one a week for 4 weeks, one a month for 12 months. A manual one can be kept up to 90 days. Backups are kept in the Standard vault.",
                    "spec": {
                      "keep": {
                        "all": 604800000000000,
                        "anchors": {
                          "month": "first",
                          "week": 0,
                          "year": 1
                        },
                        "monthly": 12,
                        "weekly": 4
                      },
                      "schedule": {
                        "at": "02:00",
                        "every": 86400000000000,
                        "start_within": 14400000000000,
                        "window_end": "06:00",
                        "window_start": "01:00"
                      },
                      "verify": {
                        "every": 2592000000000000
                      }
                    },
                    "state": "active",
                    "updated_at": "2026-09-01T08:00:00Z",
                    "vault": {
                      "id": "2d4f6a8c-0e1b-4c3d-9e5f-7a8b9c0d1e2f",
                      "immutable": false,
                      "name": "Standard"
                    },
                    "visibility": "public"
                  },
                  "policy_id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                  "project_id": "6f1e2d3c4b5a49788796a5b4c3d2e1f0",
                  "resource_ids": [
                    "e3a1b2c4-5d6e-4f70-8a9b-0c1d2e3f4a5b"
                  ],
                  "resource_kind": "instance",
                  "updated_at": "2026-10-02T09:41:12Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/PlanView"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow creating plans in its project. Or the token is not scoped to a project, so the plan would be owned by nobody.\n\n`quota_exceeded`: The resources would take the project over its resource-count or protected-bytes quota, or the project is already over its stored-bytes quota. `quota` names the limit."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: A named resource does not exist, or belongs to another project."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`partial_coverage_unaccepted`: Some disks cannot be protected and `partial_coverage` was not sent. `coverage` carries the account, its digest and the body that accepts it.\n\n`partial_coverage_stale`: `partial_coverage.digest` does not match the account the resources have now, usually because a disk was attached since it was shown. `coverage` carries the new account.\n\n`conflict`: The project already has a plan of this name, counting deleted plans not yet purged."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A field is not acceptable, and `fields` names each one. For example, `name` is missing. `resource_kind` is not `volume`, `instance` or `bucket`, or is `bucket` on a cloud with no object gateway. `resource_ids` is empty, repeats an id or names more than one bucket. `policy_id` is missing, unknown, not offered to this project or not active. `policy_id` names a policy with an instant tier for a bucket plan. `consistency` is not `crash` or `filesystem`, or is set on a bucket plan. `encryption` is not `barbican` or `none`. `encryption` asks for encryption, or is left out, on a cloud with no key manager. `labels` breaks the label rules or uses a reserved prefix. An `excluded_disks` entry matches no disk of the named resources, or more than one.\n\n`protection_refused`: A named resource has nothing that can be protected, because every one of its disks is refused or because `excluded_disks` leaves none. Or `partial_coverage.accept` is `false`. `refusals` and `coverage` say which disks and why."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`discovery_unavailable`: The service cannot inspect the resources. Resource discovery is not configured, or there is no object gateway for a bucket plan, or OpenStack would not say which project owns a resource."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Create a plan",
        "tags": [
          "Plans"
        ],
        "x-access": "member"
      }
    },
    "/v1/plans/{id}": {
      "delete": {
        "description": "Stops the plan. Its backups stay restorable until they expire, unless `with_points` is set, which deletes them too. Only an operator can delete a plan under a mandatory policy. The answer is `202` with the deletion when the plan is kept for an operator to undelete, and always with `with_points`. Without `with_points`, a plan whose repository keeps deleted plans for no grace period, or whose policy no longer exists, is removed at once. The answer is then `204` with no body.\n\nAny member of the project the token is scoped to.",
        "operationId": "deletePlan",
        "parameters": [
          {
            "description": "The plan's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` deletes the plan's backups with it. Each is hidden at once and erased at the earliest date its repository lets it go, and the answer is always `202` with `deletion_id` and `points`. Without it the backups stay restorable until they expire, and the answer is `202` with `plan_id`, `purge_after` and `detail`. But when the plan's repository keeps deleted plans for no grace period, or the plan's policy no longer exists, the plan is removed at once and the answer is `204` with no body.",
            "in": "query",
            "name": "with_points",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "deletion_id": "8e7d6c5b-4a3f-4e2d-9c1b-0a9f8e7d6c5b",
                  "detail": "this plan and its backups are hidden and take no more backups; the last of them is erased at 2026-10-09T10:20:00Z, the earliest date the repository lets it go. An operator can undelete the plan, and its backups with it, until then.",
                  "plan_id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                  "points": 58,
                  "purge_after": "2026-10-09T10:20:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DeletedView"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow deleting plans. Or the plan is under a mandatory policy and the token is not an operator's."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No plan has this id, it belongs to another project, or it is already deleted."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: `with_points` is `true` and a legal hold covers one of the plan's backups."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Delete a plan",
        "tags": [
          "Plans"
        ],
        "x-access": "member"
      },
      "get": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "getPlan",
        "parameters": [
          {
            "description": "The plan's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "consistency": "filesystem",
                  "coverage": {
                    "accepted_at": "2026-10-02T09:41:12Z",
                    "accepted_by": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                    "digest": "sha256:3b8e5f1a9c2d4e6f8a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f",
                    "excluded": [
                      {
                        "detail": "left out of this plan by its owner",
                        "effect": "data_loss",
                        "kind": "volume",
                        "locator": "/dev/vdc",
                        "name": "volume-0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d",
                        "reason": "excluded_by_owner",
                        "resource_id": "0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d",
                        "resource_kind": "instance",
                        "size_bytes": 4398046511104
                      }
                    ],
                    "scope": "partial"
                  },
                  "created_at": "2026-10-02T09:41:12Z",
                  "domain_id": "default",
                  "enabled": true,
                  "encryption": "barbican",
                  "excluded_disks": [
                    {
                      "resource_id": "0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d"
                    }
                  ],
                  "id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                  "labels": {
                    "env": "prod",
                    "team": "web"
                  },
                  "last_run_at": "2026-10-02T02:00:00Z",
                  "missed_ticks": 0,
                  "name": "web-tier",
                  "next_run_at": "2026-10-03T02:00:00Z",
                  "policy": {
                    "created_at": "2026-09-01T08:00:00Z",
                    "description": "Nightly backups kept for a year.",
                    "id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                    "mandatory": false,
                    "manual": {
                      "keep_at_least": 604800000000000,
                      "keep_at_most": 7776000000000000,
                      "max_per_day": 3
                    },
                    "name": "standard",
                    "sentence": "Every day at 02:00. Each backup is kept for 7 days, one a week for 4 weeks, one a month for 12 months. A manual one can be kept up to 90 days. Backups are kept in the Standard vault.",
                    "spec": {
                      "keep": {
                        "all": 604800000000000,
                        "anchors": {
                          "month": "first",
                          "week": 0,
                          "year": 1
                        },
                        "monthly": 12,
                        "weekly": 4
                      },
                      "schedule": {
                        "at": "02:00",
                        "every": 86400000000000,
                        "start_within": 14400000000000,
                        "window_end": "06:00",
                        "window_start": "01:00"
                      },
                      "verify": {
                        "every": 2592000000000000
                      }
                    },
                    "state": "active",
                    "updated_at": "2026-09-01T08:00:00Z",
                    "vault": {
                      "id": "2d4f6a8c-0e1b-4c3d-9e5f-7a8b9c0d1e2f",
                      "immutable": false,
                      "name": "Standard"
                    },
                    "visibility": "public"
                  },
                  "policy_id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                  "project_id": "6f1e2d3c4b5a49788796a5b4c3d2e1f0",
                  "resource_ids": [
                    "e3a1b2c4-5d6e-4f70-8a9b-0c1d2e3f4a5b"
                  ],
                  "resource_kind": "instance",
                  "retired_resources": [
                    {
                      "leaves_on": "2027-09-01T02:00:00Z",
                      "points": 14,
                      "resource_id": "7a6b5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
                      "retired_at": "2026-09-20T11:03:44Z"
                    }
                  ],
                  "updated_at": "2026-10-02T09:41:12Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/PlanView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow reading plans."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No plan has this id, or it belongs to another project. Or it is deleted and the token is not an operator's."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a plan",
        "tags": [
          "Plans"
        ],
        "x-access": "reader"
      },
      "patch": {
        "description": "Renames a plan, pauses or resumes it, changes what it protects or moves it to another policy. A resource taken out of a plan is retired: its backups stop, and the ones it has stay restorable until they expire.\n\nAny member of the project the token is scoped to.",
        "operationId": "updatePlan",
        "parameters": [
          {
            "description": "The plan's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "enabled": true,
                "excluded_disks": [],
                "name": "web-tier-prod",
                "partial_coverage": {
                  "accept": true,
                  "digest": "sha256:7c1d9e2f4a6b8c0d1e3f5a7b9c1d3e5f7a9b1c3d5e7f9a1b3c5d7e9f1a3b5c7d"
                },
                "resource_ids": [
                  "e3a1b2c4-5d6e-4f70-8a9b-0c1d2e3f4a5b",
                  "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/PatchPlanReq"
              }
            }
          },
          "description": "The changes to make. Every field is optional and an absent field is left as it is. `resource_ids` replaces the whole set. A resource left out is retired, and an empty array retires them all and pauses the plan. Changing `resource_ids` or `excluded_disks` makes the service inspect the resources again, with the same partial-coverage exchange as creation. `partial_coverage` is refused without one of them.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "consistency": "filesystem",
                  "coverage": {
                    "accepted_at": "2026-10-02T10:15:03Z",
                    "accepted_by": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                    "digest": "sha256:7c1d9e2f4a6b8c0d1e3f5a7b9c1d3e5f7a9b1c3d5e7f9a1b3c5d7e9f1a3b5c7d",
                    "excluded": [
                      {
                        "detail": "compute host \"cmp-07\" is declared local-disk",
                        "effect": "data_loss",
                        "kind": "root",
                        "locator": "_disk",
                        "name": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a_disk",
                        "reason": "local_disk_ephemeral",
                        "resource_id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
                        "resource_kind": "instance",
                        "size_bytes": 85899345920
                      }
                    ],
                    "scope": "partial"
                  },
                  "created_at": "2026-09-12T09:41:12Z",
                  "domain_id": "default",
                  "enabled": true,
                  "encryption": "barbican",
                  "id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                  "last_run_at": "2026-10-02T02:00:00Z",
                  "missed_ticks": 0,
                  "name": "web-tier-prod",
                  "next_run_at": "2026-10-03T02:00:00Z",
                  "policy": {
                    "created_at": "2026-09-01T08:00:00Z",
                    "id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                    "mandatory": false,
                    "name": "standard",
                    "sentence": "Every day at 02:00. Each backup is kept for 7 days, one a month for 12 months.",
                    "spec": {
                      "keep": {
                        "all": 604800000000000,
                        "anchors": {
                          "month": "first",
                          "week": 0,
                          "year": 1
                        },
                        "monthly": 12
                      },
                      "schedule": {
                        "at": "02:00",
                        "every": 86400000000000
                      }
                    },
                    "state": "active",
                    "updated_at": "2026-09-01T08:00:00Z",
                    "visibility": "public"
                  },
                  "policy_id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                  "project_id": "6f1e2d3c4b5a49788796a5b4c3d2e1f0",
                  "refusals": [
                    {
                      "detail": "compute host \"cmp-07\" is declared local-disk",
                      "reason": "local_disk_ephemeral",
                      "resource_id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
                      "resource_kind": "instance"
                    }
                  ],
                  "resource_ids": [
                    "e3a1b2c4-5d6e-4f70-8a9b-0c1d2e3f4a5b",
                    "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a"
                  ],
                  "resource_kind": "instance",
                  "updated_at": "2026-10-02T10:15:03Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/PlanView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow changing plans. Or the plan is under a mandatory policy, the token is not an operator's, and `enabled` is `false` or `resource_ids` is empty for a plan that is running.\n\n`quota_exceeded`: The added resources, or the move to the new policy, would take the project over its quota."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No plan has this id, or it belongs to another project, or it is deleted. Or a resource named in `resource_ids` does not exist or belongs to another project."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`partial_coverage_unaccepted`: The new resource set has disks that cannot be protected, or a disk that cannot be protected has appeared since the plan's account was accepted, and no acceptance was sent.\n\n`partial_coverage_stale`: `partial_coverage.digest` does not match the account the resources have now.\n\n`conflict`: The project already has another plan of the new name."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A field is not acceptable, and `fields` names each one. For example, `name` is empty. `policy_id` is unknown, not offered to this project or not active. `policy_id` names a policy with an instant tier for a bucket plan. A project-scoped token moves the plan to a policy that offers less protection than its current one. `consistency` is not `crash` or `filesystem`, or is set on a bucket plan. `resource_ids` names more than one bucket. An `excluded_disks` entry matches no disk, or more than one. `partial_coverage` is sent without a non-empty `resource_ids` or `excluded_disks`.\n\n`protection_refused`: A resource has nothing that can be protected. Or `partial_coverage.accept` is `false`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`discovery_unavailable`: The change needs the resources inspected again and the service cannot inspect them right now."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Change a plan",
        "tags": [
          "Plans"
        ],
        "x-access": "member"
      }
    },
    "/v1/plans/{id}/missed-ticks": {
      "get": {
        "description": "Scheduled backups that did not run, and why.\n\nAny reader of the project the token is scoped to.",
        "operationId": "listPlanMissedTicks",
        "parameters": [
          {
            "description": "The plan's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "items": [
                    {
                      "deadline": "2026-10-01T06:00:00Z",
                      "reason": "start_within_elapsed",
                      "recorded_at": "2026-10-01T06:00:12Z",
                      "scheduled_for": "2026-10-01T02:00:00Z"
                    }
                  ],
                  "limit": 50
                },
                "schema": {
                  "$ref": "#/components/schemas/MissedTickViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow reading plans."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No plan has this id, it belongs to another project, or it is deleted."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List a plan's missed backups",
        "tags": [
          "Plans"
        ],
        "x-access": "reader"
      }
    },
    "/v1/plans/{id}/refusals": {
      "get": {
        "description": "Every resource or disk the plan has been refused, why, and what to change.\n\nAny reader of the project the token is scoped to.",
        "operationId": "listPlanRefusals",
        "parameters": [
          {
            "description": "The plan's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "coverage": {
                    "accepted_at": "2026-10-02T10:15:03Z",
                    "accepted_by": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                    "digest": "sha256:7c1d9e2f4a6b8c0d1e3f5a7b9c1d3e5f7a9b1c3d5e7f9a1b3c5d7e9f1a3b5c7d",
                    "excluded": [
                      {
                        "detail": "compute host \"cmp-07\" is declared local-disk",
                        "effect": "data_loss",
                        "kind": "root",
                        "locator": "_disk",
                        "name": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a_disk",
                        "reason": "local_disk_ephemeral",
                        "resource_id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
                        "resource_kind": "instance",
                        "size_bytes": 85899345920
                      }
                    ],
                    "scope": "partial"
                  },
                  "plan_id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                  "refusals": [
                    {
                      "detail": "compute host \"cmp-07\" is declared local-disk",
                      "reason": "local_disk_ephemeral",
                      "resource_id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
                      "resource_kind": "instance"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/RefusalsResponse"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow reading plans."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No plan has this id, it belongs to another project, or it is deleted."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List what a plan could not protect",
        "tags": [
          "Plans"
        ],
        "x-access": "reader"
      }
    },
    "/v1/plans/{id}/sla": {
      "get": {
        "description": "The recovery point each resource has actually achieved, the success rate, and the oldest backup that can still be restored.\n\nAny reader of the project the token is scoped to.",
        "operationId": "getPlanSLA",
        "parameters": [
          {
            "description": "The plan's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "consistency_achieved": "filesystem",
                  "consistency_promised": "filesystem",
                  "coverage": {
                    "resources": 1,
                    "scope": "full"
                  },
                  "plan_id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                  "points_downgraded": 0,
                  "sla": {
                    "actual_rpo": 29520000000000,
                    "consecutive_failures": 0,
                    "immutably_protected_since": "2025-10-01T02:04:11Z",
                    "oldest_restorable_point": "2025-10-01T02:04:11Z",
                    "success_rate_30d": 96.7
                  },
                  "unavailable": {
                    "rpo_compliance_pct": "this plan's policy states no recovery point objective",
                    "rpo_target": "this plan's policy states no recovery point objective"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/PlanSLAResponse"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow reading plans."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No plan has this id, it belongs to another project, or it is deleted."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a plan's measured protection",
        "tags": [
          "Plans"
        ],
        "x-access": "reader"
      }
    },
    "/v1/plans/{id}/undelete": {
      "post": {
        "description": "Brings back a deleted plan as it was, running or paused, with the backups that were deleted with it.\n\nOperator only: needs a system-scoped token.",
        "operationId": "undeletePlan",
        "parameters": [
          {
            "description": "The deleted plan's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "consistency": "filesystem",
                  "coverage": {
                    "accepted_at": "2026-10-02T09:41:12Z",
                    "accepted_by": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                    "digest": "sha256:3b8e5f1a9c2d4e6f8a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f",
                    "excluded": [
                      {
                        "detail": "left out of this plan by its owner",
                        "effect": "data_loss",
                        "kind": "volume",
                        "locator": "/dev/vdc",
                        "name": "volume-0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d",
                        "reason": "excluded_by_owner",
                        "resource_id": "0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d",
                        "resource_kind": "instance",
                        "size_bytes": 4398046511104
                      }
                    ],
                    "scope": "partial"
                  },
                  "created_at": "2026-10-02T09:41:12Z",
                  "dedup_domain": "w-5f2a9c0e7b4d1e8f3a6c9b2d5e8f1a4c",
                  "domain_id": "default",
                  "enabled": true,
                  "encryption": "barbican",
                  "excluded_disks": [
                    {
                      "resource_id": "0c9b8a7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d"
                    }
                  ],
                  "id": "4f3c2a9e-8b1d-4c6e-9a7f-2d5e8c1b3a60",
                  "labels": {
                    "env": "prod",
                    "team": "web"
                  },
                  "missed_ticks": 0,
                  "name": "web-tier",
                  "next_run_at": "2026-10-03T02:00:00Z",
                  "policy": {
                    "created_at": "2026-09-01T08:00:00Z",
                    "description": "Nightly backups kept for a year.",
                    "id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                    "mandatory": false,
                    "manual": {
                      "keep_at_least": 604800000000000,
                      "keep_at_most": 7776000000000000,
                      "max_per_day": 3
                    },
                    "name": "standard",
                    "sentence": "Every day at 02:00. Each backup is kept for 7 days, one a week for 4 weeks, one a month for 12 months. A manual one can be kept up to 90 days. Backups are kept in the Standard vault.",
                    "spec": {
                      "keep": {
                        "all": 604800000000000,
                        "anchors": {
                          "month": "first",
                          "week": 0,
                          "year": 1
                        },
                        "monthly": 12,
                        "weekly": 4
                      },
                      "schedule": {
                        "at": "02:00",
                        "every": 86400000000000,
                        "start_within": 14400000000000,
                        "window_end": "06:00",
                        "window_start": "01:00"
                      },
                      "verify": {
                        "every": 2592000000000000
                      }
                    },
                    "state": "active",
                    "updated_at": "2026-09-01T08:00:00Z",
                    "vault": {
                      "id": "2d4f6a8c-0e1b-4c3d-9e5f-7a8b9c0d1e2f",
                      "immutable": false,
                      "name": "Standard"
                    },
                    "visibility": "public"
                  },
                  "policy_id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
                  "project_id": "6f1e2d3c4b5a49788796a5b4c3d2e1f0",
                  "resource_ids": [
                    "e3a1b2c4-5d6e-4f70-8a9b-0c1d2e3f4a5b"
                  ],
                  "resource_kind": "instance",
                  "updated_at": "2026-10-02T09:41:12Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/PlanView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not an operator's."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No plan has this id, the id is not a plan id at all, or the plan has already been purged."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The plan is not deleted."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Bring back a deleted plan",
        "tags": [
          "Plans"
        ],
        "x-access": "operator"
      }
    },
    "/v1/points": {
      "get": {
        "description": "Backups in state `deleted` are left out unless `state` asks for them.\n\nAny reader of the project the token is scoped to.",
        "operationId": "listBackups",
        "parameters": [
          {
            "description": "Operator only: act on this project instead of the token's own.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only these backups. Repeat for several.",
            "in": "query",
            "name": "id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups taken by this plan.",
            "in": "query",
            "name": "plan_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups of this server, volume or bucket, or that hold one of its disks.",
            "in": "query",
            "name": "resource_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups taken for this protection group.",
            "in": "query",
            "name": "group_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups of this kind: `instance`, `volume` or `bucket`. Repeat for several.",
            "in": "query",
            "name": "kind",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups in this state: `pending`, `committed`, `verified`, `broken`, `expiring`, `lock_held` or `deleted`. Repeat for several.",
            "in": "query",
            "name": "state",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups scheduled at or after this RFC 3339 time.",
            "in": "query",
            "name": "from",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups scheduled at or before this RFC 3339 time.",
            "in": "query",
            "name": "to",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups whose id or resource id contains this text.",
            "in": "query",
            "name": "q",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The field to sort by: `scheduled_for`, `committed_at`, `expires_at`, `state`, `resource_id`, `referenced_bytes`, `logical_allocated_bytes` or `last_verified_at`.",
            "in": "query",
            "name": "sort",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`asc`, the default, or `desc`. Used with `sort`.",
            "in": "query",
            "name": "order",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` adds `total`, the number of backups matching the filter.",
            "in": "query",
            "name": "total",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "Operator only: `true` lists deleted backups instead.",
            "in": "query",
            "name": "deleted",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PointViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not list backups. Or `project_id` names a project it may not read. Or `deleted=true` was asked for without operator rights."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List backups",
        "tags": [
          "Backups"
        ],
        "x-access": "reader"
      },
      "post": {
        "description": "Takes a backup outside the schedule. Name `plan_id` to run a plan, for every resource it protects or for those in `resource_ids`. Name `resource_kind`, `resource_id` and `vault_id` to back up one server or volume into a vault. The backup is kept until `keep_until`, or until deleted with `until_deleted` where the vault offers it, within the vault's terms.\n\nAny member of the project the token is scoped to.",
        "operationId": "createBackup",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "keep_until": "2026-11-01T00:00:00Z",
                "plan_id": "0b8e6a52-3c1d-4b7e-9f0a-6d2c4e8a1f37",
                "resource_ids": [
                  "c2a7f0e4-1b3d-4e5f-8a9b-0c1d2e3f4a5b"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreatePointReq"
              }
            }
          },
          "description": "The backup to take. With `plan_id`, the plan's resources, or those named in `resource_ids`, are backed up under the plan's policy into its vault. Without it, `resource_kind`, `resource_id` and `vault_id` (or `policy_id`) back up one server or volume of the token's project. The answer is 202 with the jobs queued. Repeating the same request within the same second answers 200 with the same jobs. A vault can cap manual backups per day, and a request past the cap is refused with 429 `rate_limited`.",
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "job_id": "8c1f4b7e-2d5a-4c8f-9b1e-4a7d0c3f6b9e",
                  "keep_until": "2026-11-01T00:00:00Z",
                  "plan_id": "0b8e6a52-3c1d-4b7e-9f0a-6d2c4e8a1f37",
                  "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "runs": [
                    {
                      "job_id": "8c1f4b7e-2d5a-4c8f-9b1e-4a7d0c3f6b9e",
                      "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                      "resource_id": "c2a7f0e4-1b3d-4e5f-8a9b-0c1d2e3f4a5b",
                      "state": "pending"
                    }
                  ],
                  "scheduled_for": "2026-10-02T09:14:07Z",
                  "state": "pending",
                  "vault": {
                    "id": "3b6d9f2e-5a7c-4e1b-8d3f-6a9c2e5b8d1f",
                    "immutable": false,
                    "name": "Standard"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/PointCreatedView"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not take backups in this project. Or the plan's policy keeps its backups where no vault is offered. Or the vault takes no manual backups.\n\n`quota_exceeded`: The project is over its backup quota."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The plan does not exist or is not visible to this token. Or the server or volume does not exist in the token's project."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The plan is deleted, or names no usable protection policy. Or the resource is in more than one plan that keeps its backups in this vault, so `plan_id` must say which. Or the resource is encrypted with its own key in a plan that keeps it elsewhere or names no policy. Or a backup of one resource was asked for with a token scoped to no project. Or one of the resource's disks cannot be read, so it cannot be backed up in full outside a plan.\n\n`placement_unsatisfiable`: The plan's policy cannot be served right now. `unsatisfied` says what is wrong."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A field is not acceptable, and `fields` names each one. `plan_id` is not a UUID and no `resource_id` was named. `plan_id` came with `resource_kind`, `resource_id`, `vault_id`, `policy_id` or `consistency`. `resource_ids` names a resource the plan does not protect. `resource_kind` is not `instance` or `volume`. `vault_id` is not a UUID, or names no vault this project is offered. `vault_id` and `policy_id` were both sent, or neither was. `policy_id` names no usable policy, or one whose storage no vault offers. `consistency` is not `crash` or `filesystem`, or differs from the level of the plan the resource is in. `keep_until` is outside the vault's bounds, is missing where there is no default, or was sent with `until_deleted`. `until_deleted` is not offered by the vault."
          },
          "429": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Too Many Requests.\n\n`rate_limited`: The vault's daily allowance of manual backups is used up, for the plan or for this project outside its plans."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`discovery_unavailable`: A backup of one resource was asked for, and the service cannot inspect the resource or prove which project owns it."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Back up now",
        "tags": [
          "Backups"
        ],
        "x-access": "member"
      }
    },
    "/v1/points/{id}": {
      "delete": {
        "description": "When the repository keeps deleted backups for a while, or the backup is still locked, the backup is hidden at once, not erased; it is purged no earlier than `purge_after`, and an operator can bring it back until then. A lock never refuses this delete, only when the backup actually leaves. Otherwise it is erased now, and `job_id` names the job erasing it. A backup under a legal hold cannot be deleted.\n\nAny member of the project the token is scoped to.",
        "operationId": "deleteBackup",
        "parameters": [
          {
            "description": "The id of the backup to delete.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "detail": "this point is hidden, not erased; it is purged no earlier than 2026-10-09T09:40:00Z. An operator can undelete it until then.",
                  "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "purge_after": "2026-10-09T09:40:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DeletedView"
                }
              }
            },
            "description": "Accepted"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not delete backups in this project. Or the backup's plan is under a mandatory policy and the token is not an operator's."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no backup has it, or it is not visible to this token."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: A legal hold covers the backup, its plan or its project. Or the backup is already deleted. Or its state is not `committed`, `verified`, `broken` or `lock_held`, for example because it is still being taken or already being erased. Or it was taken in another cloud, where it has to be deleted."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Delete a backup",
        "tags": [
          "Backups"
        ],
        "x-access": "member"
      },
      "get": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "getBackup",
        "parameters": [
          {
            "description": "The id of the backup.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "chunk_repo_id": "e4f7a0c3-6b9d-4f2e-8a5c-1d4f7b0e3a6c",
                  "committed_at": "2026-10-02T02:19:48Z",
                  "consistency": "filesystem",
                  "coverage": {
                    "scope": "full"
                  },
                  "created_at": "2026-10-02T02:00:00Z",
                  "disks": [
                    {
                      "boot_index": 0,
                      "chunk_count": 1830,
                      "device": "/dev/vda",
                      "hole_count": 3290,
                      "image": "c2a7f0e4-1b3d-4e5f-8a9b-0c1d2e3f4a5b_disk",
                      "pool": "vms",
                      "resource_id": "c2a7f0e4-1b3d-4e5f-8a9b-0c1d2e3f4a5b",
                      "role": "root",
                      "size_bytes": 21474836480
                    },
                    {
                      "boot_index": 1,
                      "chunk_count": 9214,
                      "device": "/dev/vdb",
                      "hole_count": 16386,
                      "image": "volume-7e9a1b2c-3d4e-4f5a-8b6c-9d0e1f2a3b4c",
                      "pool": "volumes",
                      "resource_id": "7e9a1b2c-3d4e-4f5a-8b6c-9d0e1f2a3b4c",
                      "role": "volume",
                      "size_bytes": 107374182400
                    }
                  ],
                  "domain_id": "default",
                  "expires_at": "2026-11-01T02:00:00Z",
                  "freeze_duration_ms": 412,
                  "id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "last_verified_at": "2026-10-02T06:41:10Z",
                  "metrics": {
                    "logical_allocated_bytes": 46322794496,
                    "logical_provisioned_bytes": 128849018880,
                    "referenced_bytes": 19730006016,
                    "scanned_bytes": 3221225472,
                    "t_ceph_read_ms": 61200,
                    "t_diff_ms": 5210,
                    "t_hash_compress_ms": 48800,
                    "t_repo_write_ms": 39100
                  },
                  "periods": {
                    "daily": "2026-10-02",
                    "monthly": "2026-10-01"
                  },
                  "plan_id": "0b8e6a52-3c1d-4b7e-9f0a-6d2c4e8a1f37",
                  "policy_id": "a3d9e1f4-7b2c-4e8a-9c1d-5f6e7a8b9c0d",
                  "project_id": "4f1c2a7d9b3e4e0a8c5d6b7a8f9e0d1c",
                  "resource_id": "c2a7f0e4-1b3d-4e5f-8a9b-0c1d2e3f4a5b",
                  "resource_kind": "instance",
                  "roles": [
                    "daily",
                    "monthly"
                  ],
                  "scheduled_for": "2026-10-02T02:00:00Z",
                  "started_at": "2026-10-02T02:00:31Z",
                  "state": "verified",
                  "tier_state": "cold_only",
                  "updated_at": "2026-10-02T06:41:10Z",
                  "vault": {
                    "id": "3b6d9f2e-5a7c-4e1b-8d3f-6a9c2e5b8d1f",
                    "immutable": false,
                    "name": "Standard"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/PointView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not read backups in the backup's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no backup has it. Or the backup belongs to another project. Or it is deleted or `lock_held` and the token is not an operator's."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a backup",
        "tags": [
          "Backups"
        ],
        "x-access": "reader"
      }
    },
    "/v1/points/{id}/document": {
      "get": {
        "description": "The backup's self-describing record, read from the repository rather than from this cloud's database. It answers for a backup this cloud has never seen.\n\nOperator only: needs a system-scoped token.",
        "operationId": "getBackupDocument",
        "parameters": [
          {
            "description": "The id of the backup to read. It need not be known to this cloud.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Required. The repository to read the record from, by name or id.",
            "in": "query",
            "name": "repository",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The dedup domain to look in. By default every domain this cloud holds a key for is searched.",
            "in": "query",
            "name": "domain",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is neither an operator's nor a peer cloud's."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID. Or no repository has that name or id. Or the repository holds no record of this backup in the dedup domains searched."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`domain_key_missing`: This cloud holds no key for the dedup domain the record is in, or the key it holds did not write it. `detail` names the key to import.\n\n`tier_unavailable`: The repository named is an rbd tier, which keeps no records."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `repository` is missing."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`kms_unavailable`: The record is encrypted and this site has no key manager configured."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Read a backup's record from storage",
        "tags": [
          "Backups"
        ],
        "x-access": "operator"
      }
    },
    "/v1/points/{id}/metrics": {
      "get": {
        "description": "Sizes, ratios and how long each phase took. A figure that cannot be measured is left out and explained under `unavailable`. Figures that depend on other projects' data are shown to operators only.\n\nAny reader of the project the token is scoped to.",
        "operationId": "getBackupMetrics",
        "parameters": [
          {
            "description": "The id of the backup.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "freeze_duration_ms": 412,
                  "logical_allocated_bytes": 46322794496,
                  "logical_provisioned_bytes": 128849018880,
                  "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "referenced_bytes": 19730006016,
                  "scanned_bytes": 3221225472,
                  "scope": "tenant",
                  "tier_state": "cold_only",
                  "timings": {
                    "t_ceph_read_ms": 61200,
                    "t_diff_ms": 5210,
                    "t_hash_compress_ms": 48800,
                    "t_repo_write_ms": 39100
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/PointMetricsView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not read this backup's metrics."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no backup has it, or it is not visible to this token."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a backup's metrics",
        "tags": [
          "Backups"
        ],
        "x-access": "reader"
      }
    },
    "/v1/points/{id}/undelete": {
      "post": {
        "description": "Lifts a delete before the backup is erased.\n\nOperator only: needs a system-scoped token.",
        "operationId": "undeleteBackup",
        "parameters": [
          {
            "description": "The id of the deleted backup to bring back.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "chunk_repo_id": "e4f7a0c3-6b9d-4f2e-8a5c-1d4f7b0e3a6c",
                  "committed_at": "2026-09-28T02:11:40Z",
                  "consistency": "crash",
                  "coverage": {
                    "scope": "full"
                  },
                  "created_at": "2026-09-28T02:00:00Z",
                  "dedup_domain": "plain",
                  "disks": [
                    {
                      "boot_index": 0,
                      "chunk_count": 9214,
                      "hole_count": 16386,
                      "image": "volume-7e9a1b2c-3d4e-4f5a-8b6c-9d0e1f2a3b4c",
                      "manifest_key": "plain/manifests/1e4b7d0a-3c6f-4a9d-8e2b-5f8a1c4e7d0b.idx",
                      "pool": "volumes",
                      "resource_id": "7e9a1b2c-3d4e-4f5a-8b6c-9d0e1f2a3b4c",
                      "role": "volume",
                      "size_bytes": 107374182400
                    }
                  ],
                  "domain_id": "default",
                  "erases_at": "2026-10-28T02:00:00Z",
                  "expires_at": "2026-10-28T02:00:00Z",
                  "freeze_duration_ms": 0,
                  "id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "metrics": {
                    "compression_ratio": 2.41,
                    "dedup_ratio": 1.12,
                    "entropy_delta": 0.04,
                    "exclusive_bytes": 1073741824,
                    "logical_allocated_bytes": 38646317056,
                    "logical_provisioned_bytes": 107374182400,
                    "mean_entropy": 5.83,
                    "new_bytes": 805306368,
                    "referenced_bytes": 16106127360,
                    "scanned_bytes": 2147483648,
                    "t_ceph_read_ms": 40210,
                    "t_diff_ms": 3120,
                    "t_hash_compress_ms": 31800,
                    "t_repo_write_ms": 25460,
                    "transferred_bytes": 805306368
                  },
                  "periods": {
                    "daily": "2026-09-28"
                  },
                  "plan_id": "0b8e6a52-3c1d-4b7e-9f0a-6d2c4e8a1f37",
                  "point_key": "plain/points/5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11.json",
                  "policy_id": "a3d9e1f4-7b2c-4e8a-9c1d-5f6e7a8b9c0d",
                  "project_id": "4f1c2a7d9b3e4e0a8c5d6b7a8f9e0d1c",
                  "resource_id": "7e9a1b2c-3d4e-4f5a-8b6c-9d0e1f2a3b4c",
                  "resource_kind": "volume",
                  "roles": [
                    "daily"
                  ],
                  "scheduled_for": "2026-09-28T02:00:00Z",
                  "started_at": "2026-09-28T02:00:12Z",
                  "state": "committed",
                  "tier_state": "cold_only",
                  "updated_at": "2026-10-02T10:02:55Z",
                  "vault": {
                    "id": "3b6d9f2e-5a7c-4e1b-8d3f-6a9c2e5b8d1f",
                    "immutable": false,
                    "name": "Standard"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/PointView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not an operator's."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no backup has it."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The backup is not deleted. Or its purge date has passed and it is already being erased."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Bring back a deleted backup",
        "tags": [
          "Backups"
        ],
        "x-access": "operator"
      }
    },
    "/v1/points/{id}/verify": {
      "post": {
        "description": "Re-reads and checks all of the backup's data instead of waiting for the regular verification. The backup's state changes only when the check finishes. The answer is the job doing it.\n\nAny member of the project the token is scoped to.",
        "operationId": "verifyBackup",
        "parameters": [
          {
            "description": "The id of the backup to verify.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "attempts": 0,
                  "created_at": "2026-10-02T09:20:00Z",
                  "domain_id": "default",
                  "id": "9d2a5c8f-3e6b-4d9a-8c2f-5b8e1d4a7c0f",
                  "kind": "verify",
                  "max_attempts": 5,
                  "next_attempt_at": "2026-10-02T09:20:00Z",
                  "plan_id": "0b8e6a52-3c1d-4b7e-9f0a-6d2c4e8a1f37",
                  "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "progress": {
                    "bytes_done": 0,
                    "bytes_total": 0,
                    "chunks_done": 0,
                    "chunks_total": 0,
                    "eta_seconds": 0,
                    "percent": 0,
                    "phase": "",
                    "skipped_bytes": 0,
                    "stored_bps": 0,
                    "stored_bytes": 0,
                    "throughput_bps": 0
                  },
                  "project_id": "4f1c2a7d9b3e4e0a8c5d6b7a8f9e0d1c",
                  "repo_id": "e4f7a0c3-6b9d-4f2e-8a5c-1d4f7b0e3a6c",
                  "site_id": "6b9e2d5a-8c1f-4e4b-9a7d-0c3f6a9e2d5b",
                  "state": "pending",
                  "updated_at": "2026-10-02T09:20:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/JobView"
                }
              }
            },
            "description": "Accepted"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not verify backups in this project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no backup has it, or it is not visible to this token."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The backup's state is not `committed`, `verified`, `broken` or `lock_held`, so there is nothing written to re-read. Or it is kept only on the instant tier and not in a repository."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Verify a backup now",
        "tags": [
          "Backups"
        ],
        "x-access": "member"
      }
    },
    "/v1/policies": {
      "get": {
        "description": "A project sees the `active` and `deprecated` policies it may name, each described in a sentence. An operator sees every policy with its storage, assignments and plan count.\n\nAny reader of the project the token is scoped to.",
        "operationId": "listPolicies",
        "parameters": [
          {
            "description": "Only policies in this state: `active`, `deprecated` or `retired`. A project sees only `active` and `deprecated` policies, so asking for `retired` answers an empty list.",
            "in": "query",
            "name": "state",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` lists only the policies the service cannot honour right now.",
            "in": "query",
            "name": "unsatisfied",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `policy:list` permission."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List policies",
        "tags": [
          "Policies"
        ],
        "x-access": "reader"
      },
      "post": {
        "description": "Sets when backups run, how long each is kept and where it is stored. It can also set the recovery and verification it promises. A `public` policy is offered to every project. An `assigned` one is offered only to the domains and projects in `assigned_to`.\n\nOperator only: needs a system-scoped token.",
        "operationId": "createPolicy",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "assigned_to": {
                  "domains": [
                    "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a"
                  ]
                },
                "description": "Daily backups kept for a week, weekly and monthly ones kept longer, and a copy in a second repository.",
                "name": "Gold",
                "spec": {
                  "keep": {
                    "all": "168h",
                    "anchors": {
                      "month": "first",
                      "week": "sunday"
                    },
                    "monthly": 12,
                    "weekly": 4
                  },
                  "schedule": {
                    "at": "02:00",
                    "every": "24h",
                    "jitter": "30m",
                    "start_within": "4h"
                  },
                  "tiers": {
                    "backup": {
                      "repository_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60"
                    },
                    "copy": {
                      "keep": {
                        "all": "720h",
                        "monthly": 12
                      },
                      "repository_id": "1a2b3c4d-5e6f-4a1b-8c2d-3e4f5a6b7c8d"
                    }
                  },
                  "verify": {
                    "every": "168h"
                  }
                },
                "visibility": "assigned"
              },
              "schema": {
                "$ref": "#/components/schemas/CreatePolicyReq"
              }
            }
          },
          "description": "The new policy. `name` and `spec` are required, and in `spec` the `schedule`, `keep` and `tiers.backup` blocks.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "assigned_to": {
                    "domains": [
                      "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a"
                    ]
                  },
                  "created_at": "2026-09-14T08:12:44Z",
                  "description": "Daily backups kept for a week, weekly and monthly ones kept longer, and a copy in a second repository.",
                  "id": "5b0c6a8e-7d1f-4c39-9a52-2f6f1d3e8b71",
                  "mandatory": false,
                  "manual": {
                    "keep_at_least": 604800000000000,
                    "keep_at_most": 7776000000000000,
                    "max_per_day": 2,
                    "until_deleted": true
                  },
                  "name": "Gold",
                  "plan_count": 0,
                  "sentence": "Every day at 02:00. Each backup is kept for a week, one a week for 4 weeks, one a month for 12 months. A manual one can be kept up to 90 days, or until you delete it. Backups are kept in the Standard vault.",
                  "spec": {
                    "keep": {
                      "all": 604800000000000,
                      "anchors": {
                        "month": "first",
                        "week": 0,
                        "year": 1
                      },
                      "monthly": 12,
                      "weekly": 4
                    },
                    "schedule": {
                      "at": "02:00",
                      "every": 86400000000000,
                      "jitter": 1800000000000,
                      "start_within": 14400000000000
                    },
                    "tiers": {
                      "backup": {
                        "repository_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60"
                      },
                      "copy": {
                        "keep": {
                          "all": 2592000000000000,
                          "anchors": {
                            "month": "first",
                            "week": 0,
                            "year": 1
                          },
                          "monthly": 12
                        },
                        "repository_id": "1a2b3c4d-5e6f-4a1b-8c2d-3e4f5a6b7c8d"
                      }
                    },
                    "verify": {
                      "every": 604800000000000
                    }
                  },
                  "state": "active",
                  "updated_at": "2026-09-14T08:12:44Z",
                  "vault": {
                    "id": "3d9f1b7e-2a4c-4e6b-8d1f-5a7c9e1b3d5f",
                    "immutable": false,
                    "name": "Standard"
                  },
                  "visibility": "assigned"
                },
                "schema": {
                  "$ref": "#/components/schemas/PolicyView"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `policy:write` permission. Or `assigned_to` is given and the token lacks `policy:assign`."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: Another policy already has this name."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A field is missing or not acceptable, and `fields` names each one. `name` is empty. `visibility` or `state` has another value. A duration or id does not parse. The schedule breaks a rule of its own, such as a window that opens and closes at the same time. The ladder keeps nothing. A repository is missing, not registered or of the wrong kind. The copy repository is the backup repository. The retention is shorter than a locked repository's floor, or longer than its ceiling or the cloud's longest lock. The recovery site is missing or not registered. No repository is reachable from the recovery site. `transport` is not the rung's own. `rpo_target` is below the cadence on a `cold` or `warm` rung, or below the mirror snapshot interval this cloud drives on a `hot` one."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Create a policy",
        "tags": [
          "Policies"
        ],
        "x-access": "operator"
      }
    },
    "/v1/policies/{id}": {
      "delete": {
        "description": "Only a policy with no plans can be deleted. Retire one that has plans instead.\n\nOperator only: needs a system-scoped token.",
        "operationId": "deletePolicy",
        "parameters": [
          {
            "description": "The policy's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `policy:write` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no policy has it."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: A plan names the policy, counting deleted plans still inside their purge grace. Retire the policy instead."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Delete a policy",
        "tags": [
          "Policies"
        ],
        "x-access": "operator"
      },
      "get": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "getPolicy",
        "parameters": [
          {
            "description": "The policy's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-14T08:12:44Z",
                  "description": "Daily backups kept for a week, weekly and monthly ones kept longer, and a copy in a second repository.",
                  "id": "5b0c6a8e-7d1f-4c39-9a52-2f6f1d3e8b71",
                  "mandatory": false,
                  "manual": {
                    "keep_at_least": 604800000000000,
                    "keep_at_most": 7776000000000000,
                    "max_per_day": 2,
                    "until_deleted": true
                  },
                  "name": "Gold",
                  "sentence": "Every day at 02:00. Each backup is kept for a week, one a week for 4 weeks, one a month for 12 months. A manual one can be kept up to 90 days, or until you delete it. Backups are kept in the Standard vault.",
                  "spec": {
                    "keep": {
                      "all": 604800000000000,
                      "anchors": {
                        "month": "first",
                        "week": 0,
                        "year": 1
                      },
                      "monthly": 12,
                      "weekly": 4
                    },
                    "schedule": {
                      "at": "02:00",
                      "every": 86400000000000,
                      "jitter": 1800000000000,
                      "start_within": 14400000000000
                    },
                    "verify": {
                      "every": 604800000000000
                    }
                  },
                  "state": "active",
                  "updated_at": "2026-09-14T08:12:44Z",
                  "vault": {
                    "id": "3d9f1b7e-2a4c-4e6b-8d1f-5a7c9e1b3d5f",
                    "immutable": false,
                    "name": "Standard"
                  },
                  "visibility": "assigned"
                },
                "schema": {
                  "$ref": "#/components/schemas/PolicyView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `policy:list` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no policy has it. Or the policy is neither public nor assigned to the token's project or domain."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a policy",
        "tags": [
          "Policies"
        ],
        "x-access": "reader"
      },
      "patch": {
        "description": "A block the request does not mention is kept. An optional block is removed with its `clear_` flag. Changes apply to the backups taken from now on. With `apply_to_existing`, the existing backups that are not locked are re-dated too, and `applied` says how many changed. Moving an expiry earlier needs the `policy:shorten` permission.\n\nOperator only: needs a system-scoped token.",
        "operationId": "updatePolicy",
        "parameters": [
          {
            "description": "The policy's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` also re-dates the existing backups of the policy's plans to the new retention. Manual backups are left alone. Moving any expiry earlier needs the `policy:shorten` permission. The answer then also carries `applied`.",
            "in": "query",
            "name": "apply_to_existing",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "description": "Twice-daily backups kept for two weeks, weekly and monthly ones kept longer, and a copy in a second repository.",
                "spec": {
                  "keep": {
                    "all": "336h",
                    "monthly": 12,
                    "weekly": 8
                  },
                  "schedule": {
                    "at": "02:00",
                    "every": "12h"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/PatchPolicyReq"
              }
            }
          },
          "description": "The fields to change. A field or spec block left out is kept.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "assigned_to": {
                    "domains": [
                      "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a"
                    ]
                  },
                  "created_at": "2026-09-14T08:12:44Z",
                  "description": "Twice-daily backups kept for two weeks, weekly and monthly ones kept longer, and a copy in a second repository.",
                  "id": "5b0c6a8e-7d1f-4c39-9a52-2f6f1d3e8b71",
                  "mandatory": false,
                  "manual": {
                    "keep_at_least": 1209600000000000,
                    "keep_at_most": 7776000000000000,
                    "max_per_day": 2,
                    "until_deleted": true
                  },
                  "name": "Gold",
                  "plan_count": 12,
                  "sentence": "Every 12 hours. Each backup is kept for 2 weeks, one a week for 8 weeks, one a month for 12 months. A manual one can be kept up to 90 days, or until you delete it. Backups are kept in the Standard vault.",
                  "spec": {
                    "keep": {
                      "all": 1209600000000000,
                      "anchors": {
                        "month": "first",
                        "week": 0,
                        "year": 1
                      },
                      "monthly": 12,
                      "weekly": 8
                    },
                    "schedule": {
                      "at": "02:00",
                      "every": 43200000000000
                    },
                    "tiers": {
                      "backup": {
                        "repository_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60"
                      },
                      "copy": {
                        "keep": {
                          "all": 2592000000000000,
                          "anchors": {
                            "month": "first",
                            "week": 0,
                            "year": 1
                          },
                          "monthly": 12
                        },
                        "repository_id": "1a2b3c4d-5e6f-4a1b-8c2d-3e4f5a6b7c8d"
                      }
                    },
                    "verify": {
                      "every": 604800000000000
                    }
                  },
                  "state": "active",
                  "updated_at": "2026-10-01T15:40:09Z",
                  "vault": {
                    "id": "3d9f1b7e-2a4c-4e6b-8d1f-5a7c9e1b3d5f",
                    "immutable": false,
                    "name": "Standard"
                  },
                  "visibility": "assigned"
                },
                "schema": {
                  "$ref": "#/components/schemas/UpdatePolicyResp"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `policy:write` permission. Or `assigned_to` is given and the token lacks `policy:assign`. Or `apply_to_existing` would move an existing backup's expiry earlier and the token lacks `policy:shorten`."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no policy has it."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: Another policy already has the new name."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A field is not acceptable, and `fields` names each one. `name` is empty. `visibility` has another value. A `clear_` flag is sent with the block it clears. The changed terms fail a check made on creation."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Change a policy",
        "tags": [
          "Policies"
        ],
        "x-access": "operator"
      }
    },
    "/v1/policies/{id}/assign": {
      "post": {
        "description": "Offers the policy to more domains or projects or, with `remove`, withdraws it from them.\n\nOperator only: needs a system-scoped token.",
        "operationId": "assignPolicy",
        "parameters": [
          {
            "description": "The policy's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` withdraws the policy from the domains and projects named, instead of offering it to them.",
            "in": "query",
            "name": "remove",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "projects": [
                  "9a7c3e1f5b2d4a6c8e0f1a3b5c7d9e2f"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/AssignmentReq"
              }
            }
          },
          "description": "The domains and projects to offer the policy to or, with `remove=true`, to withdraw it from. Name at least one.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "assigned_to": {
                    "domains": [
                      "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a"
                    ],
                    "projects": [
                      "9a7c3e1f5b2d4a6c8e0f1a3b5c7d9e2f"
                    ]
                  },
                  "created_at": "2026-09-14T08:12:44Z",
                  "description": "Daily backups kept for a week, weekly and monthly ones kept longer, and a copy in a second repository.",
                  "id": "5b0c6a8e-7d1f-4c39-9a52-2f6f1d3e8b71",
                  "mandatory": false,
                  "manual": {
                    "keep_at_least": 604800000000000,
                    "keep_at_most": 7776000000000000,
                    "max_per_day": 2,
                    "until_deleted": true
                  },
                  "name": "Gold",
                  "plan_count": 12,
                  "sentence": "Every day at 02:00. Each backup is kept for a week, one a week for 4 weeks, one a month for 12 months. A manual one can be kept up to 90 days, or until you delete it. Backups are kept in the Standard vault.",
                  "spec": {
                    "keep": {
                      "all": 604800000000000,
                      "anchors": {
                        "month": "first",
                        "week": 0,
                        "year": 1
                      },
                      "monthly": 12,
                      "weekly": 4
                    },
                    "schedule": {
                      "at": "02:00",
                      "every": 86400000000000,
                      "jitter": 1800000000000,
                      "start_within": 14400000000000
                    },
                    "tiers": {
                      "backup": {
                        "repository_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60"
                      }
                    },
                    "verify": {
                      "every": 604800000000000
                    }
                  },
                  "state": "active",
                  "updated_at": "2026-09-14T08:12:44Z",
                  "vault": {
                    "id": "3d9f1b7e-2a4c-4e6b-8d1f-5a7c9e1b3d5f",
                    "immutable": false,
                    "name": "Standard"
                  },
                  "visibility": "assigned"
                },
                "schema": {
                  "$ref": "#/components/schemas/PolicyView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `policy:assign` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no policy has it."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: The body names no domain and no project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Assign a policy",
        "tags": [
          "Policies"
        ],
        "x-access": "operator"
      }
    },
    "/v1/policies/{id}/deprecate": {
      "post": {
        "description": "No new plan may name it. Plans already under it keep running.\n\nOperator only: needs a system-scoped token.",
        "operationId": "deprecatePolicy",
        "parameters": [
          {
            "description": "The policy's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "assigned_to": {
                    "domains": [
                      "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a"
                    ]
                  },
                  "created_at": "2026-09-14T08:12:44Z",
                  "description": "Daily backups kept for a week, weekly and monthly ones kept longer, and a copy in a second repository.",
                  "id": "5b0c6a8e-7d1f-4c39-9a52-2f6f1d3e8b71",
                  "mandatory": false,
                  "manual": {
                    "keep_at_least": 604800000000000,
                    "keep_at_most": 7776000000000000,
                    "max_per_day": 2,
                    "until_deleted": true
                  },
                  "name": "Gold",
                  "plan_count": 12,
                  "sentence": "Every day at 02:00. Each backup is kept for a week, one a week for 4 weeks, one a month for 12 months. A manual one can be kept up to 90 days, or until you delete it. Backups are kept in the Standard vault.",
                  "spec": {
                    "keep": {
                      "all": 604800000000000,
                      "anchors": {
                        "month": "first",
                        "week": 0,
                        "year": 1
                      },
                      "monthly": 12,
                      "weekly": 4
                    },
                    "schedule": {
                      "at": "02:00",
                      "every": 86400000000000,
                      "jitter": 1800000000000,
                      "start_within": 14400000000000
                    },
                    "tiers": {
                      "backup": {
                        "repository_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60"
                      }
                    },
                    "verify": {
                      "every": 604800000000000
                    }
                  },
                  "state": "deprecated",
                  "updated_at": "2026-09-28T09:20:51Z",
                  "vault": {
                    "id": "3d9f1b7e-2a4c-4e6b-8d1f-5a7c9e1b3d5f",
                    "immutable": false,
                    "name": "Standard"
                  },
                  "visibility": "assigned"
                },
                "schema": {
                  "$ref": "#/components/schemas/PolicyView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `policy:write` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no policy has it."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The policy is retired."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Deprecate a policy",
        "tags": [
          "Policies"
        ],
        "x-access": "operator"
      }
    },
    "/v1/policies/{id}/retire": {
      "post": {
        "description": "Retires a policy with no plans, or moves every plan onto `successor` in one step. The successor must be another active policy, no weaker, and offered to every project whose plans move. Backups keep their dates through the move.\n\nOperator only: needs a system-scoped token.",
        "operationId": "retirePolicy",
        "parameters": [
          {
            "description": "The policy's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The policy to move every plan onto, in one step. It must be another active policy, no weaker than this one, and offered to every project whose plans move. Without it, only a policy with no plans can be retired.",
            "in": "query",
            "name": "successor",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "assigned_to": {
                    "domains": [
                      "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a"
                    ]
                  },
                  "created_at": "2026-09-14T08:12:44Z",
                  "description": "Daily backups kept for a week, weekly and monthly ones kept longer, and a copy in a second repository.",
                  "id": "5b0c6a8e-7d1f-4c39-9a52-2f6f1d3e8b71",
                  "mandatory": false,
                  "manual": {
                    "keep_at_least": 604800000000000,
                    "keep_at_most": 7776000000000000,
                    "max_per_day": 2,
                    "until_deleted": true
                  },
                  "moved_plans": 12,
                  "name": "Gold",
                  "plan_count": 0,
                  "sentence": "Every day at 02:00. Each backup is kept for a week, one a week for 4 weeks, one a month for 12 months. A manual one can be kept up to 90 days, or until you delete it. Backups are kept in the Standard vault.",
                  "spec": {
                    "keep": {
                      "all": 604800000000000,
                      "anchors": {
                        "month": "first",
                        "week": 0,
                        "year": 1
                      },
                      "monthly": 12,
                      "weekly": 4
                    },
                    "schedule": {
                      "at": "02:00",
                      "every": 86400000000000,
                      "jitter": 1800000000000,
                      "start_within": 14400000000000
                    },
                    "tiers": {
                      "backup": {
                        "repository_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60"
                      }
                    },
                    "verify": {
                      "every": 604800000000000
                    }
                  },
                  "state": "retired",
                  "updated_at": "2026-09-30T10:02:17Z",
                  "vault": {
                    "id": "3d9f1b7e-2a4c-4e6b-8d1f-5a7c9e1b3d5f",
                    "immutable": false,
                    "name": "Standard"
                  },
                  "visibility": "assigned"
                },
                "schema": {
                  "$ref": "#/components/schemas/RetirePolicyResp"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `policy:write` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no policy has it."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: No `successor` is named and plans still name the policy, counting deleted plans inside their purge grace. `detail` says how many there are."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `successor` is not a UUID. It names no policy, this policy itself, or a policy that is not active. It is not offered to every project whose plans would move, and `fields` lists those projects. Or it is weaker: it keeps backups for less time or keeps fewer of them, writes to a repository with a weaker lock, promises a lower recovery rung, runs less often, or is not mandatory where this one is. `detail` says which."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Retire a policy",
        "tags": [
          "Policies"
        ],
        "x-access": "operator"
      }
    },
    "/v1/projects/{id}/offboard": {
      "post": {
        "description": "Deletes every plan of a project that is leaving, with its backups. The answer gives the date the last of the project's data leaves storage, as `erased_by`.\n\nOperator only: needs a system-scoped token.",
        "operationId": "offboardProject",
        "parameters": [
          {
            "description": "The Keystone id of the project to offboard.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "deletion_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
                  "detail": "3 plans and 412 backups of project 6f1e2d3c4b5a49788796a5b4c3d2e1f0 are deleted and hidden from it; the last object leaves the repository by 2026-11-01T00:00:00Z. An operator can undelete each plan until then.",
                  "erased_by": "2026-11-01T00:00:00Z",
                  "plans": 3,
                  "points": 412,
                  "project_id": "6f1e2d3c4b5a49788796a5b4c3d2e1f0"
                },
                "schema": {
                  "$ref": "#/components/schemas/OffboardView"
                }
              }
            },
            "description": "Accepted"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not an operator's."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: A legal hold covers one of the project's backups. Nothing is deleted."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Offboard a project",
        "tags": [
          "Plans"
        ],
        "x-access": "operator"
      }
    },
    "/v1/protection-groups": {
      "get": {
        "description": "Any member of the project the token is scoped to.",
        "operationId": "listProtectionGroups",
        "parameters": [
          {
            "description": "Operator only: list this project's groups instead of the token's own. A system-scoped token without it lists every project's groups.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`primary` lists only groups created here, `recovery` only the recoveries registered here. Absent lists both.",
            "in": "query",
            "name": "role",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`sla` attaches each group's measured protection as `sla`, the same object GET /v1/protection-groups/{id}/sla returns. `readiness` attaches what a recovery would use and refuse now, as GET /v1/protection-groups/{id}/readiness returns it. Both may be given, comma-separated. No other value is accepted.",
            "in": "query",
            "name": "include",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token has no member role in the project. Or `project_id` names another project and the token may not read other projects."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `role` is neither `primary` nor `recovery`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: `include=readiness` was asked for and disaster recovery is not enabled here."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List protection groups",
        "tags": [
          "Protection groups"
        ],
        "x-access": "member"
      },
      "post": {
        "description": "Groups servers that recover together at a disaster recovery site, under the protection policy whose recovery section is their promise. Each member has a boot order and an optional health check that must pass before the next tier boots, and each must already be protected by a plan under that policy. `network_map` says where members land in a recovery; a test lands every member on the isolated network its test policy builds. Where the group recovers to, at which rung, how far behind and how fast are the policy's, never the group's. A new group runs here: its role is `primary`, its custody `active` and its custody epoch 1. With `recovery_of`, an operator instead registers the recovery here of a group another region created, as GET /v1/repositories/{id}/inventory?view=groups lists it. The recovery takes its members, custody epoch and source project from the group's newest document, starts with custody `standby` and state `unprotected`, and binds every member to `target_project_id`, with a standby target when the group is `warm`. A member already bound to that project is left on its binding; one bound anywhere else refuses the registration. Later versions of the document are followed: a member added at home is bound here, and one removed at home leaves the group and keeps its binding.\n\nAny member of the project the token is scoped to.",
        "operationId": "createProtectionGroup",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "members": [
                  {
                    "boot_order": 1,
                    "health": {
                      "port": 5432,
                      "type": "tcp"
                    },
                    "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                    "name": "db-01"
                  },
                  {
                    "boot_order": 2,
                    "health": {
                      "path": "/healthz",
                      "port": 8080,
                      "timeout": "15s",
                      "type": "http"
                    },
                    "instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                    "name": "app-01"
                  }
                ],
                "name": "payments-prod",
                "network_map": {
                  "prod-net": "dr-net"
                },
                "policy_id": "2a1b3c4d-5e6f-4708-8991-aabbccddeeff"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateGroupReq"
              }
            }
          },
          "description": "The group's name, policy, members and network maps; or the group to recover and where.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-02T08:15:00Z",
                  "custody": "active",
                  "custody_epoch": 1,
                  "domain_id": "default",
                  "id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                  "members": [
                    {
                      "active_instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "boot_order": 1,
                      "health": {
                        "port": 5432,
                        "retries": 10,
                        "timeout": 10000000000,
                        "type": "tcp"
                      },
                      "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "name": "db-01"
                    },
                    {
                      "active_instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                      "boot_order": 2,
                      "health": {
                        "expect": 200,
                        "path": "/healthz",
                        "port": 8080,
                        "retries": 10,
                        "timeout": 15000000000,
                        "type": "http"
                      },
                      "instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                      "name": "app-01"
                    }
                  ],
                  "name": "payments-prod",
                  "network_map": {
                    "prod-net": "dr-net"
                  },
                  "policy_id": "2a1b3c4d-5e6f-4708-8991-aabbccddeeff",
                  "project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                  "role": "primary",
                  "site_id": "a7c1e2d3-4b5f-4a6e-8d7c-9b0a1f2e3d4c",
                  "state": "protected",
                  "updated_at": "2026-10-02T08:15:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/GroupView"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow creating protection groups in its project, or the request names `recovery_of` or `mapping_profile_id` and the token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`group_document_not_found`: No document for `recovery_of.group_uid` has been read from `recovery_of.repository_id`."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`key_manager_missing`: The group's document is in an encrypted dedup domain of the repository, and this cloud runs without a key manager, so it can never read it. `detail` names the domain and the cloud that wrote it.\n\n`domain_key_missing`: The group's document is in a dedup domain of the repository that this cloud holds no key for. `detail` names the domain to import.\n\n`conflict`: The project already has a group with this name. Or the group is already held here, or was created by this cloud. Or the target project exists here but is disabled, or does not exist.\n\n`binding_conflict`: A member of the group being registered is bound to another project here, or to the target project without the standby a `warm` group needs.\n\n`tier_unavailable`: `recovery_of.repository_id` names an RBD tier, which holds no group documents."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `name` is missing. `policy_id` is missing, is not a UUID, or names no policy this project may use. There are no members, or a member has no `instance_id` or appears twice. A health check has an unknown `type`, is `tcp` or `http` without a `port` from 1 to 65535, has a `timeout` under one second, or has negative `retries`, or its `timeout` is not a duration. A field that belongs to a recovery is sent without `recovery_of`. An accepted gap is empty, or `mapping_profile_id` names no recovery profile. With `recovery_of`: members are sent, `target_project_id` is missing, a uuid is malformed or names nothing here, `origin_cloud` disagrees with the document, `test_every` is not a duration, a `warm` group is sent without `standby.pool`, or a group that is not `warm` is sent with `standby`.\n\n`policy_has_no_recovery`: The policy states no recovery section, so a group under it would be promised nothing.\n\n`member_not_under_group_policy`: A member is protected by no plan, or only by plans under other policies. `fields` names each such member as `members.<instance_id>`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: The request names `recovery_of` and disaster recovery is not enabled here."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Create a protection group",
        "tags": [
          "Protection groups"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}": {
      "delete": {
        "description": "Stops protecting the servers as a group. The servers themselves are not touched. Only a group with no disaster recovery run acting on it can be deleted. The group's runs and their journals go with it; the record of what those runs created stays until each item marked for teardown has been removed. Deleting a recovery, which only an operator may do, also deletes the recovery bindings it created; bindings it found already in place stay, and so do the backups already adopted and any standby.\n\nA recovery whose runs built servers, volumes, ports or floating addresses here and kept them is not deleted while they are still held, because nothing here could see or remove them afterwards: retire it first, which removes what a superseded recover built, or, where the workload runs here, fail it back first. With `keep_resources=true` it is deleted anyway and every one of them is left where it is, as the project's own; the deletion's audit record names them.\n\nAny member of the project the token is scoped to.",
        "operationId": "deleteProtectionGroup",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` deletes a recovery even though objects its runs built and kept are still held, and leaves each of them in the project as it is. Ignored for a group created here.",
            "in": "query",
            "name": "keep_resources",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not change groups in the group's project, or the group is a recovery and the token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`group_busy`: A run is acting on the group, so `active_run_id` is set or its state is that of a running operation.\n\n`group_holds_resources`: The group is a recovery, and servers, volumes, ports or floating addresses its runs built and kept are still held, and `keep_resources` is not `true`. `fields` names each as `resources.<id>`, and `detail` says whether to retire or fail back first."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Delete a protection group",
        "tags": [
          "Protection groups"
        ],
        "x-access": "member"
      },
      "get": {
        "description": "One protection group. A recovery also carries the recovery bindings that adopt its members' backups, when the token may read bindings.\n\nAny member of the project the token is scoped to.",
        "operationId": "getProtectionGroup",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-02T08:15:00Z",
                  "custody": "active",
                  "custody_epoch": 1,
                  "domain_id": "default",
                  "id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                  "members": [
                    {
                      "active_instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "boot_order": 1,
                      "health": {
                        "port": 5432,
                        "retries": 10,
                        "timeout": 10000000000,
                        "type": "tcp"
                      },
                      "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "name": "db-01"
                    },
                    {
                      "active_instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                      "boot_order": 2,
                      "health": {
                        "expect": 200,
                        "path": "/healthz",
                        "port": 8080,
                        "retries": 10,
                        "timeout": 15000000000,
                        "type": "http"
                      },
                      "instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                      "name": "app-01"
                    }
                  ],
                  "name": "payments-prod",
                  "network_map": {
                    "prod-net": "dr-net"
                  },
                  "policy_id": "2a1b3c4d-5e6f-4708-8991-aabbccddeeff",
                  "project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                  "role": "primary",
                  "site_id": "a7c1e2d3-4b5f-4a6e-8d7c-9b0a1f2e3d4c",
                  "state": "degraded",
                  "state_changed_at": "2026-10-02T08:21:40Z",
                  "updated_at": "2026-10-02T08:21:40Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/GroupView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token has no member role in the group's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a protection group",
        "tags": [
          "Protection groups"
        ],
        "x-access": "member"
      },
      "patch": {
        "description": "Renames a group or changes its policy, members or network maps. A new policy, and every member after the change, are checked as on create: the policy must state a recovery section, and every member must be protected by a plan under it. A group with a disaster recovery run acting on it cannot be changed, with one exception: a request that changes only a recovery's `test_every` is accepted while a run holds it, so a schedule can always be changed or stopped. A recovery is changed only by an operator: its name, policy, network maps, recovery profile, test policy, test cadence and accepted gaps. Its members follow its home's document, and its standby target stays as registered. A group created here also takes the accepted gaps and the recovery profile its failback resolves a rebuilt member through, when the member's original is gone; the profile only from an operator.\n\nAny member of the project the token is scoped to.",
        "operationId": "updateProtectionGroup",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "members": [
                  {
                    "boot_order": 1,
                    "health": {
                      "port": 5432,
                      "type": "tcp"
                    },
                    "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                    "name": "db-01"
                  },
                  {
                    "boot_order": 2,
                    "health": {
                      "path": "/healthz",
                      "port": 8080,
                      "timeout": "15s",
                      "type": "http"
                    },
                    "instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                    "name": "app-01"
                  },
                  {
                    "boot_order": 3,
                    "instance_id": "e4da3b7f-bbce-4345-9d77-7b2c1a6e3f10",
                    "name": "web-01"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/PatchGroupReq"
              }
            }
          },
          "description": "The fields to change. Maps and the member list are replaced whole.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-02T08:15:00Z",
                  "custody": "active",
                  "custody_epoch": 1,
                  "domain_id": "default",
                  "id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                  "members": [
                    {
                      "active_instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "boot_order": 1,
                      "health": {
                        "port": 5432,
                        "retries": 10,
                        "timeout": 10000000000,
                        "type": "tcp"
                      },
                      "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "name": "db-01"
                    },
                    {
                      "active_instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                      "boot_order": 2,
                      "health": {
                        "expect": 200,
                        "path": "/healthz",
                        "port": 8080,
                        "retries": 10,
                        "timeout": 15000000000,
                        "type": "http"
                      },
                      "instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                      "name": "app-01"
                    },
                    {
                      "active_instance_id": "e4da3b7f-bbce-4345-9d77-7b2c1a6e3f10",
                      "boot_order": 3,
                      "health": {
                        "retries": 10,
                        "timeout": 10000000000,
                        "type": "none"
                      },
                      "instance_id": "e4da3b7f-bbce-4345-9d77-7b2c1a6e3f10",
                      "name": "web-01"
                    }
                  ],
                  "name": "payments-prod",
                  "network_map": {
                    "prod-net": "dr-net"
                  },
                  "policy_id": "2a1b3c4d-5e6f-4708-8991-aabbccddeeff",
                  "project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                  "role": "primary",
                  "site_id": "a7c1e2d3-4b5f-4a6e-8d7c-9b0a1f2e3d4c",
                  "state": "protected",
                  "updated_at": "2026-10-02T09:02:11Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/GroupView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not change groups in the group's project, or the group is a recovery and the token is not a system-scoped admin token, or the request changes a group's `mapping_profile_id` and the token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`group_busy`: A run is acting on the group, so `active_run_id` is set or its state is that of a running operation, and the request changes something other than a recovery's `test_every`.\n\n`conflict`: Another group in the project already has the new name."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `name` is empty. The new members break a rule of group creation. `policy_id` is not a UUID or names no policy this project may use. The members change on a group that names no policy, or whose policy no longer exists, and the request names none. `bubble_policy_id` or `test_every` is sent for a group created here, or members are sent for a recovery. `test_every` is not a duration, an accepted gap is empty, or a recovery profile or test policy does not exist.\n\n`policy_has_no_recovery`: The policy states no recovery section.\n\n`member_not_under_group_policy`: A member is not protected by a plan under the group's policy, or under the new one."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Change a protection group",
        "tags": [
          "Protection groups"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/arm": {
      "delete": {
        "description": "Withdraws the group's standing delegation and revokes it, so this region can no longer act in the group's project for its recovery runs. Answers the same whether or not the group was armed.\n\nAny member of the project the token is scoped to.",
        "operationId": "disarmProtectionGroup",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not arm groups in the group's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Disarm a protection group",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      },
      "post": {
        "description": "Gives this region a standing delegation to act in the group's project for its recovery runs: to build servers, ports and volumes there for a recovery, or to stop and lock them for a release. The delegation is the caller's own roles on the project, so only a member of the project can give it, with a token scoped to that project and issued by a password rather than an application credential: a system-scoped token is refused, because the identity service delegates only roles the caller holds in the project, and a token scoped to another project does not find the group. The alternative to arming is an operator granting this service's own user a role in the project, which runs then use instead. Arming again replaces the delegation and revokes the old one. Readiness reports which of the two a run would use, and a group with neither, or whose delegation is about to expire.\n\nAny member of the project the token is scoped to.",
        "operationId": "armProtectionGroup",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "expires_in": "2160h"
              },
              "schema": {
                "$ref": "#/components/schemas/ArmReq"
              }
            }
          },
          "description": "How long the delegation lasts. Optional.",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "armed": true,
                  "armed_until": "2026-12-31T09:00:00Z",
                  "created_at": "2026-10-02T08:15:00Z",
                  "custody": "active",
                  "custody_epoch": 1,
                  "doc_published_at": "2026-10-02T08:20:00Z",
                  "doc_version": 2,
                  "domain_id": "default",
                  "group_uid": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                  "id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                  "members": [
                    {
                      "active_instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "boot_order": 1,
                      "health": {
                        "port": 5432,
                        "retries": 10,
                        "timeout": 10000000000,
                        "type": "tcp"
                      },
                      "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "name": "db-01"
                    }
                  ],
                  "name": "payments-prod",
                  "network_map": {
                    "prod-net": "dr-net"
                  },
                  "policy_id": "2a1b3c4d-5e6f-4708-8991-aabbccddeeff",
                  "project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                  "role": "primary",
                  "site_id": "a7c1e2d3-4b5f-4a6e-8d7c-9b0a1f2e3d4c",
                  "state": "protected",
                  "updated_at": "2026-10-02T09:00:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/GroupView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type.\n\n`restore_trust_refused`: The token was issued from an application credential, or carries no token to delegate from."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not arm groups in the group's project.\n\n`restore_trust_refused`: The caller holds no role on the group's project to delegate."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `expires_in` is not a duration from `1h` to `8784h`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here.\n\n`service_unavailable`: This service cannot act through a delegation, so arming would promise runs it cannot carry out."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Arm a protection group",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/failback-bindings": {
      "post": {
        "description": "At a group's home after a failover, proposes and then creates the recovery bindings that let this region adopt the backups of the servers the other region recovered, so a failback can write them back into the originals. The proposal is exactly the set the other region's newest record of recovering the group names: one binding per member, from the repository the record was read from, into the group's project. A member already bound into the project keeps its binding; one bound into another project is refused with `binding_conflict`. With `dry_run=true` the request answers the proposal and creates nothing. Operator only.\n\nOperator only: needs a system-scoped token.",
        "operationId": "confirmFailbackBindings",
        "parameters": [
          {
            "description": "The id of the protection group at its home.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` answers the proposal and creates nothing.",
            "in": "query",
            "name": "dry_run",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "reason": "preparing to fail payments back home"
              },
              "schema": {
                "$ref": "#/components/schemas/FailbackBindingsReq"
              }
            }
          },
          "description": "Why. The body may be omitted.",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "bindings": [
                    {
                      "binding": {
                        "asserted_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                        "created_at": "0001-01-01T00:00:00Z",
                        "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                        "origin_cloud": "cloud-b",
                        "points_adopted": 0,
                        "repository_id": "3f2b1c4d-0000-4000-8000-000000000001",
                        "source_project": "7c1e2d3f4a5b46c7d8e9f0a1b2c3d4e5",
                        "source_resource_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
                        "target_domain_id": "default",
                        "target_project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                        "updated_at": "0001-01-01T00:00:00Z"
                      },
                      "existing": false,
                      "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60"
                    }
                  ],
                  "dry_run": true,
                  "epoch": 4,
                  "from_cloud": "cloud-b",
                  "recorded_at": "2026-10-03T08:05:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/FailbackBindingsResponse"
                }
              }
            },
            "description": "OK.\n\nWith `dry_run=true`, or when every binding already existed, the proposal. Nothing was created."
          },
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "bindings": [
                    {
                      "binding": {
                        "asserted_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                        "created_at": "2026-10-03T10:00:00Z",
                        "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                        "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
                        "origin_cloud": "cloud-b",
                        "points_adopted": 0,
                        "repository_id": "3f2b1c4d-0000-4000-8000-000000000001",
                        "source_project": "7c1e2d3f4a5b46c7d8e9f0a1b2c3d4e5",
                        "source_resource_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
                        "target_domain_id": "default",
                        "target_project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                        "updated_at": "2026-10-03T10:00:00Z"
                      },
                      "existing": false,
                      "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60"
                    }
                  ],
                  "dry_run": false,
                  "epoch": 4,
                  "from_cloud": "cloud-b",
                  "recorded_at": "2026-10-03T08:05:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/FailbackBindingsResponse"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not an operator token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The group is a recovery registration; failback bindings are confirmed where the workload comes back.\n\n`no_newer_custody_elsewhere`: No record of another region recovering the group has been read here.\n\n`binding_conflict`: A recovered server is already bound into another project; `fields` names each member."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Bind the recovered servers' backups back home",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/protection-groups/{id}/mirror": {
      "get": {
        "description": "For a `hot` group, each mirrored volume image as this region reads it from its own storage: whether it holds the primary copy, the replication state of both regions, the newest complete mirror snapshot of a non-primary copy, how old the newest replicated state is against the promised `rpo_target`, and whether both regions hold a primary copy. Measured by this region every pass; empty for a group nothing mirrors.\n\nAny member of the project the token is scoped to.",
        "operationId": "getProtectionGroupMirror",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                  "images": [
                    {
                      "global_id": "8f7c7715-5101-4fb2-b2ad-17b054432349",
                      "image": "volume-5dceb4c9-6bf9-484f-97b4-06d50742a41e",
                      "lag_seconds": 312,
                      "last_complete_at": "2026-10-03T09:05:00Z",
                      "last_complete_snapshot": ".mirror.non_primary.8f7c7715-5101-4fb2-b2ad-17b054432349.1c2d",
                      "member_instance_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                      "mirror_state": "up+replaying",
                      "peer_site": "region-b",
                      "peer_state": "up+stopped",
                      "pool": "cinder-ceph",
                      "role": "non-primary",
                      "split_brain": false,
                      "state": "ok"
                    }
                  ],
                  "rpo_target_seconds": 900,
                  "rung": "hot",
                  "split_brain": false,
                  "transport": "rbd-mirror",
                  "worst_lag_seconds": 312
                },
                "schema": {
                  "$ref": "#/components/schemas/GroupMirror"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token has no member role in the group's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show how a hot protection group's volumes are mirrored",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/readiness": {
      "get": {
        "description": "Member by member, what a recovery would recover each server to and anything that would refuse it now. On a recovery this is measured here: the binding adopting each member's backups, the newest adopted backup for a `cold` group, the standby and the backup it holds for a `warm` one, how old that is against the promised `rpo_target`, whether the group is armed, and any dedup domain whose key this cloud lacks. On a group created here it is what the recovery region last reported, read from the records it writes into its own repositories, with how old that report is.\n\nAny member of the project the token is scoped to.",
        "operationId": "getProtectionGroupReadiness",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "checked_at": "2026-10-02T09:10:00Z",
                  "custody_epoch": 4,
                  "delegation": {
                    "armed": true,
                    "expires_at": "2026-12-31T09:00:00Z"
                  },
                  "doc_version": 3,
                  "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                  "group_uid": "0a1b2c3d-4e5f-4061-8273-94a5b6c7d8e9",
                  "members": [
                    {
                      "applied_point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                      "binding_id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
                      "boot_order": 1,
                      "dedup_domain": "w-3f1c9a2b7d4e5f60",
                      "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "name": "db-01",
                      "ready": true,
                      "recovery_point_at": "2026-10-02T09:00:12Z",
                      "standby_id": "7b8c9d0e-1f2a-4b3c-9d4e-5f6a7b8c9d0e",
                      "standby_state": "current"
                    },
                    {
                      "binding_id": "8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f",
                      "boot_order": 2,
                      "dedup_domain": "w-3f1c9a2b7d4e5f60",
                      "instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                      "issues": [
                        {
                          "code": "member_never_synced",
                          "detail": "no standby holds this member yet; one is registered with its first adopted point"
                        }
                      ],
                      "name": "app-01",
                      "ready": false
                    }
                  ],
                  "ready": false,
                  "role": "recovery",
                  "rung": "warm"
                },
                "schema": {
                  "$ref": "#/components/schemas/GroupReadiness"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token has no member role in the group's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show what a recovery of a protection group would use",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/recover": {
      "post": {
        "description": "Brings the group's workload up here, in the region that holds its standby copy. A `planned` recover acts only on a release the other region recorded: the newest record for the group must be a release from the region this group recovers from, at the custody epoch held here or above, with no recovery above it. It brings each member back from exactly the final backup the release took, so nothing written there is lost, and it never acts because time passed: with `wait_for_release` it waits up to that long for the record, and is refused with `release_not_observed` if none arrives. Without a release it can verify it is refused with `release_not_observed`, saying where to release. An `unplanned` recover, for when the group's home is lost or cannot be reached, needs no release. Only an operator can start one, and only by confirming with `confirm_primary_may_be_serving_writes` that the home may still be running the workload; the confirmation is audited with who gave it.\n\nAt a group's home, a recover is a failback. A `planned` one acts on the recovery region's release and writes its final backup back into each original server, in place, from the point the server held when it was released: instance ids, ports and addresses, and the home's plans carry on unchanged. Before a byte is written, each original's disks are compared with the snapshot of the point it holds, and an original that changed is refused with `return_target_diverged` \u2014 unless an operator asks for `overwrite_diverged`, which keeps what the original holds as a divergence point and then writes the whole backup over it. A member whose original no longer exists is rebuilt from the recovery region's backup as a new server, resolved through the group's own recovery profile and accepted gaps. The originals are then unlocked and started, and the plans the release paused resume. An `unplanned` recover at home takes back a group released here by starting the originals as they are, without the recovery region's consent.\n\nThe run restores each member of a `cold` group from a backup adopted here, or clones each member's standby for a `warm` one, into the group's project. It then takes custody: the group's custody here becomes `active` and its custody epoch goes up by one, which is the run's point of no return. Before it, a failure or a cancel removes everything the run built; after it, nothing is removed. It boots the members tier by tier in boot order, on the networks the group's `network_map` and recovery profile resolve, and starts a tier only once every member of the tier before it passed its health check. Last, it records the recovery in the repository for the group's home to read, and puts the recovered servers under a new backup plan with the group's policy, which the group names as `reprotect_plan_id`. The group ends `protected`.\n\nWith `dry_run=true` the request answers 200 with the plan the run would follow and starts nothing. Otherwise it answers 202 with the admitted run; follow it at GET /v1/protection-groups/{id}/runs/{run_id}. Everything that would refuse the run is checked before it is admitted, and again by the run itself before it acts: a run that finds a refusal then ends `failed` with that code in `error_code`. A refusal about members names each in `fields` as `members.<instance id>`.\n\nThe `hot` rung is refused in this release. `GET /v1/capabilities` says under `dr` and `dr_reason` whether recovery runs on this cloud.\n\nAny member of the project the token is scoped to.",
        "operationId": "recoverProtectionGroup",
        "parameters": [
          {
            "description": "The id of the protection group to recover.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` works out the plan the run would follow, answers it with 200 and starts nothing: no run, job or audit record is created and the group keeps its state. Every refusal is answered as it would be without it.",
            "in": "query",
            "name": "dry_run",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "confirm_primary_may_be_serving_writes": true,
                "max_point_age": "6h",
                "mode": "unplanned",
                "reason": "eu-ro-1 lost power; recovering payments in cloud-b",
                "recovery_point": "aligned"
              },
              "schema": {
                "$ref": "#/components/schemas/RecoverReq"
              }
            }
          },
          "description": "The mode, the confirmation, why, and how to choose each member's backup.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "dry_run": true,
                  "plan": {
                    "members": [
                      {
                        "boot_order": 1,
                        "health": {
                          "port": 5432,
                          "retries": 10,
                          "timeout": 10000000000,
                          "type": "tcp"
                        },
                        "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                        "mapping": {
                          "flavor_id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
                          "flavor_name": "m2.large",
                          "ports": [
                            {
                              "drop_fixed_ip": true,
                              "network_id": "6e7f8091-a2b3-4c4d-9e5f-60718293a4b5",
                              "source_port_id": "5d6e7f80-91a2-4b3c-8d4e-5f60718293a4"
                            }
                          ],
                          "security_groups": {
                            "groups": []
                          },
                          "target_project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                          "volumes": [
                            {
                              "source_volume_id": "7f8091a2-b3c4-4d5e-8f60-718293a4b5c6",
                              "volume_type": "ceph-ssd"
                            }
                          ]
                        },
                        "name": "db-01",
                        "point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                        "recovery_point_at": "2026-10-03T08:00:12Z"
                      },
                      {
                        "boot_order": 2,
                        "health": {
                          "expect": 200,
                          "path": "/healthz",
                          "port": 8080,
                          "retries": 10,
                          "timeout": 15000000000,
                          "type": "http"
                        },
                        "instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                        "mapping": {
                          "flavor_id": "4d5e6f7a-8b9c-4d0e-9f1a-2b3c4d5e6f7a",
                          "flavor_name": "m2.medium",
                          "ports": [
                            {
                              "drop_fixed_ip": true,
                              "network_id": "6e7f8091-a2b3-4c4d-9e5f-60718293a4b5",
                              "source_port_id": "8091a2b3-c4d5-4e6f-9071-8293a4b5c6d7"
                            }
                          ],
                          "security_groups": {
                            "groups": []
                          },
                          "target_project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                          "volumes": [
                            {
                              "source_volume_id": "91a2b3c4-d5e6-4f70-8182-93a4b5c6d7e8",
                              "volume_type": "ceph-ssd"
                            }
                          ]
                        },
                        "name": "app-01",
                        "point_id": "2a3b4c5d-6e7f-4809-9a1b-2c3d4e5f6a7b",
                        "recovery_point_at": "2026-10-03T08:04:51Z"
                      }
                    ],
                    "newest_at": "2026-10-03T08:04:51Z",
                    "oldest_at": "2026-10-03T08:00:12Z",
                    "operation": "recover",
                    "probe_flavor": "m1.small",
                    "probe_image": "c0a8f1e2-3b4d-4c5e-9f60-7182a3b4c5d6",
                    "profile_id": "4d5e6f70-8192-4a3b-9c4d-5e6f708192a3",
                    "recovery_point": "aligned",
                    "rung": "cold",
                    "spread_seconds": 279
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DRPlanResponse"
                }
              }
            },
            "description": "OK.\n\nWith `dry_run=true`, the plan the run would follow. Nothing was started."
          },
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "job_id": "4e5f6a7b-8c9d-4e0f-9a1b-3c4d5e6f7a8b",
                  "run": {
                    "confirmed_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                    "custody_epoch": {
                      "from": 3,
                      "to": 0
                    },
                    "deadline_at": "2026-10-03T22:05:00Z",
                    "exec_epoch": 0,
                    "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "id": "9c0d1e2f-3a4b-4c5d-8e6f-7a8b9c0d1e2f",
                    "job_id": "4e5f6a7b-8c9d-4e0f-9a1b-3c4d5e6f7a8b",
                    "measured": {},
                    "mode": "unplanned",
                    "operation": "recover",
                    "options": {
                      "max_point_age_seconds": 21600,
                      "recovery_point": "aligned"
                    },
                    "prior_state": "ready",
                    "reason": "eu-ro-1 lost power; recovering payments in cloud-b",
                    "requested_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                    "resume_count": 0,
                    "rung": "cold",
                    "started_at": "2026-10-03T10:05:00Z",
                    "state": "pending",
                    "updated_at": "2026-10-03T10:05:00Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RunResponse"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not recover this group. An `unplanned` recover needs a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project.\n\n`group_document_not_found`: The group's repository no longer shows the document its home writes."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`group_busy`: A run is acting on the group.\n\n`custody_not_standby`: This region does not hold the group's standby copy, because its custody here is `active` or `released`.\n\n`split_brain_risk`: The group is in `split_brain_risk`, which only a resync or an acknowledgement clears.\n\n`standby_advanced`: A member's standby moved to a newer backup while the recover was being worked out. Asking again works from the newer one.\n\n`release_not_observed`: A `planned` recover finds no release it can verify, and no `wait_for_release` was asked: nothing was released, the newest record is not a release, it came from a region this group does not recover from, or a recovery was recorded above it. The detail says where to release.\n\n`release_epoch_mismatch`: The other region released at a custody epoch below the one held here, so it is not the release this region takes the workload from."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `mode` is missing or is neither `planned` nor `unplanned`. `recovery_point` is neither `aligned` nor `newest`, or is sent with a `planned` recover. `max_point_age` is not a positive duration, or is sent with a `planned` recover. `wait_for_release` is sent with an `unplanned` recover, or is not a positive duration of at most 30 minutes. `fields` names each.\n\n`unplanned_failover_unconfirmed`: An `unplanned` recover is sent without `confirm_primary_may_be_serving_writes` set to true.\n\n`group_has_no_policy`: The group names no protection policy, so nothing says how it recovers.\n\n`policy_has_no_recovery`: The group's policy states no recovery section.\n\n`member_not_mirrored`: For a `hot` group, a member's disk is not a mirrored volume here.\n\n`image_not_found_by_global_id`: For a `hot` group, no image here carries a member's disk's mirror global id.\n\n`mirror_unavailable`: The group promises the `hot` rung and this region cannot reach its own mirrored images.\n\n`member_never_synced`: No standby holds a member of a `warm` group yet, or a mirrored image of a `hot` group holds no complete mirror snapshot.\n\n`member_has_no_recoverable_state`: No backup of a member has been adopted here, or the group has no member.\n\n`domain_key_missing`: A member's backups are encrypted under a dedup domain whose key this cloud lacks.\n\n`member_not_ready`: A member's readiness reports something else that refuses a recovery, such as `binding_missing`; `fields` gives it as `<issue code>: <detail>`. Or a member's backup cannot be read, or describes no server to build.\n\n`standby_has_no_applied_snapshot`: A member's standby holds no snapshot taken when an update finished, so nothing it holds is known to be a whole backup.\n\n`recovery_point_too_old`: A member's recovery point is older than `max_point_age`, or its age is unknown.\n\n`partial_coverage_unaccepted`: A member's backup does not hold every disk of the server, and the group's registration does not accept `partial_coverage`.\n\n`no_probe_origin`: Members declare health checks, and this region has neither a probe image to run them from nor a route to the project's networks. A check is never reduced to the server merely being up.\n\n`recovery_delegation_missing`: Nothing can be built in the group's project: the group is not armed or its delegation has expired, and the service holds no role there.\n\n`mapping_unresolved`: A member's server cannot be rebuilt here as it stands: an element such as a flavor or network does not map, and the group's `accept_gaps` does not accept it: the registration's, or at a group's home its own, for a member a failback rebuilds because its original is gone. `fields` names each member's elements.\n\n`return_target_diverged`: With `dry_run=true`, a failback finds an original server whose disks changed after the point it holds, one that is running, or one that cannot be compared and is no longer locked by the release. A started failback that finds it ends `failed` with this `error_code` before anything is written. Fail back again with `overwrite_diverged`, which keeps what each such original holds as a backup first; a resync does not apply, since it runs where the workload runs.\n\n`failback_binding_missing`: With `dry_run=true`, a failback finds a member whose final backup from the recovery region has not been adopted here: confirm the failback bindings first. A started failback waits for it, and ends `failed` with this `error_code` if it does not arrive.\n\n`unplanned_needs_originals`: An `unplanned` recover at the group's home finds a member whose original server no longer exists."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here, or this cloud lacks something the recover needs, which the detail names."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Recover a protection group",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/release": {
      "post": {
        "description": "Stops the group's workload here and hands it over to the region that recovers it, for a planned move with nothing lost. Runs where the group's custody is `active`: at its home before a failover, or at the recovery region before a failback.\n\nThe run pauses the backup plans that protect the members, locks each member's server so nobody starts it, shuts it down and waits until it is off. It then takes a final backup of every member with the server off and waits until that backup has reached every repository the other region reads, the copy repository included. Last, it records the release, with when each member stopped and its final backup, in those repositories, and the group's custody here becomes `released`. That record is the run's point of no return: before it, a failure or a cancel starts the servers again, unlocks them and resumes the plans; after it, nothing is undone. The other region then recovers the group with a `planned` recover, which acts on that record and nothing else. At the group's home, each member's final backup is kept as the point its original server holds, for the failback that returns to it.\n\nA release needs a recent report from the other region that it can recover every member, or it is refused with `recovery_region_not_ready`: releasing a workload nothing can bring up is an outage. An operator may release without one with `override_recovery_readiness`, which is audited.\n\nWith `dry_run=true` the request answers 200 with the plan the run would follow and starts nothing. Otherwise it answers 202 with the admitted run; follow it at GET /v1/protection-groups/{id}/runs/{run_id}. The `hot` rung is refused in this release.\n\nAny member of the project the token is scoped to.",
        "operationId": "releaseProtectionGroup",
        "parameters": [
          {
            "description": "The id of the protection group to release.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` works out the plan the run would follow, answers it with 200 and starts nothing. Every refusal is answered as it would be without it.",
            "in": "query",
            "name": "dry_run",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "reason": "moving payments to cloud-b for the datacenter maintenance"
              },
              "schema": {
                "$ref": "#/components/schemas/ReleaseReq"
              }
            }
          },
          "description": "Why, and whether to release without a fresh readiness report from the other region. The body may be omitted.",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "dry_run": true,
                  "plan": {
                    "members": [
                      {
                        "boot_order": 1,
                        "health": {
                          "port": 5432,
                          "type": "tcp"
                        },
                        "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                        "name": "db-01",
                        "plan_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
                        "point_id": "",
                        "running_instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60"
                      }
                    ],
                    "operation": "release",
                    "plans": [
                      "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
                    ],
                    "recovery_point": "",
                    "recovery_region": {
                      "age_seconds": 180,
                      "epoch": 3,
                      "fresh": true,
                      "from_cloud": "cloud-b",
                      "from_site": "bucharest",
                      "ready": true,
                      "recorded_at": "2026-10-03T09:57:00Z"
                    },
                    "rung": "cold",
                    "spread_seconds": 0
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DRPlanResponse"
                }
              }
            },
            "description": "OK.\n\nWith `dry_run=true`, the plan the run would follow. Nothing was started."
          },
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "job_id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
                  "run": {
                    "custody_epoch": {
                      "from": 3,
                      "to": 0
                    },
                    "deadline_at": "2026-10-03T22:00:00Z",
                    "exec_epoch": 0,
                    "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
                    "job_id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
                    "measured": {},
                    "operation": "release",
                    "options": {},
                    "prior_state": "protected",
                    "reason": "moving payments to cloud-b for the datacenter maintenance",
                    "requested_by": "6c0f2d8e4a1b4c3d9e7f5a2b1c0d9e8f",
                    "resume_count": 0,
                    "rung": "cold",
                    "started_at": "2026-10-03T10:00:00Z",
                    "state": "pending",
                    "updated_at": "2026-10-03T10:00:00Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RunResponse"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not release this group. `override_recovery_readiness` needs an operator token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`group_busy`: A run is acting on the group.\n\n`custody_not_active`: The workload does not run here, because the group's custody here is `standby` or `released`.\n\n`split_brain_risk`: The group is in `split_brain_risk`, which only a resync or an acknowledgement clears.\n\n`recovery_region_not_ready`: The other region's newest readiness report is missing, older than this service accepts, for another custody epoch, or says it could not recover every member, which `fields` names."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`member_not_under_group_policy`: A member's server is protected by no plan under the group's policy, so no final backup can be taken of it.\n\n`member_disk_needs_running_guest`: A member has a disk that can only be read through its running server, and a release takes its final backup with the server off.\n\n`member_not_mirrored`: For a `hot` group, a member has no mirrored image recorded, or one of its images is not mirror-enabled or not primary here, so it cannot be demoted. `fields` names each.\n\n`mirror_unavailable`: The group promises the `hot` rung and this region cannot reach its own mirrored images."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here, or this cloud cannot stop, lock or start servers, which the detail names."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Release a protection group",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/resync": {
      "post": {
        "description": "Stands this region's copy of the workload down after the other region took it over without a release, keeping what was written here as a backup first. Runs where the group's custody is `active` and the other region has recorded taking the group at the custody epoch held here or above, which is what puts a group in `split_brain_risk`; without such a record it is refused with `no_newer_custody_elsewhere`. Only an operator can start one, and only by confirming with `confirm_discard_local_writes`.\n\nThe run pauses the members' plans, locks and stops their servers, then asks of each member whether anything changed after the point the other region recovered from, by comparing its disks with that point's snapshot; a member that cannot be compared is taken to have changed. Each member that changed is backed up first, labelled with the cloud and epoch it diverged from, so what was written here can be restored later like any backup. The group's custody here then becomes `standby` at the other region's epoch, which is the run's point of no return, and the resync is recorded for the other region to read. At the group's home, each member then holds that backup, or the point the other region recovered from when nothing changed, for the failback that returns to it. A failure before the point of no return starts the servers again and leaves the group as it was, `split_brain_risk` included.\n\nOperator only: needs a system-scoped token.",
        "operationId": "resyncProtectionGroup",
        "parameters": [
          {
            "description": "The id of the protection group to resync.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` works out the plan the run would follow, answers it with 200 and starts nothing.",
            "in": "query",
            "name": "dry_run",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "confirm_discard_local_writes": true,
                "reason": "the other region took over during the outage"
              },
              "schema": {
                "$ref": "#/components/schemas/ResyncReq"
              }
            }
          },
          "description": "The acknowledgement, whether to skip the backup of what diverged, and why.",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "dry_run": true,
                  "plan": {
                    "custody_epoch_to": 4,
                    "evidence": {
                      "cloud": "cloud-b",
                      "epoch": 4,
                      "recorded_at": "2026-10-03T08:05:00Z",
                      "run_id": "9c0d1e2f-3a4b-4c5d-8e6f-7a8b9c0d1e2f"
                    },
                    "members": [
                      {
                        "boot_order": 1,
                        "health": {
                          "port": 5432,
                          "type": "tcp"
                        },
                        "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                        "known_point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                        "plan_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
                        "point_id": "",
                        "running_instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60"
                      }
                    ],
                    "operation": "resync",
                    "plans": [
                      "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
                    ],
                    "recovery_point": "",
                    "rung": "cold",
                    "spread_seconds": 0
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DRPlanResponse"
                }
              }
            },
            "description": "OK.\n\nWith `dry_run=true`, the plan the run would follow. Nothing was started."
          },
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "job_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a80",
                  "run": {
                    "confirmed_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                    "custody_epoch": {
                      "from": 3,
                      "to": 0
                    },
                    "deadline_at": "2026-10-03T22:00:00Z",
                    "exec_epoch": 0,
                    "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "id": "4d5e6f7a-8b9c-4d0e-9f1a-2b3c4d5e6f70",
                    "job_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a80",
                    "measured": {},
                    "operation": "resync",
                    "options": {},
                    "prior_state": "split_brain_risk",
                    "reason": "the other region took over during the outage",
                    "requested_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                    "resume_count": 0,
                    "rung": "cold",
                    "started_at": "2026-10-03T10:00:00Z",
                    "state": "pending",
                    "updated_at": "2026-10-03T10:00:00Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RunResponse"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not an operator token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`group_busy`: A run is acting on the group.\n\n`custody_not_active`: Nothing runs here to stand down, because the group's custody here is `standby` or `released`.\n\n`no_newer_custody_elsewhere`: The other region has recorded no recovery of the group at the custody epoch held here or above."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`resync_unconfirmed`: `confirm_discard_local_writes` is missing or false.\n\n`member_not_under_group_policy`: A member's server is protected by no plan under the group's policy, so what diverged on it cannot be backed up; an operator may skip that backup by name.\n\n`image_not_found_by_global_id`: For a `hot` group, no image here carries a member's disk's mirror global id, so it cannot be resynced."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here, or this cloud cannot stop, lock or start servers, which the detail names."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Resync a protection group",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/protection-groups/{id}/retire": {
      "post": {
        "description": "Removes what a superseded recovery left here once the other region has taken the workload back: the stopped, locked servers a recover built, the ports and floating addresses it gave them. Their volumes are never deleted: each is kept when its server is deleted and then released from the block storage service, leaving the underlying image where it is, named in the run's resources as an `unmanaged_volume` for an operator to remove. Runs only once this region has read the other region's record of recovering the group at the custody epoch held here or above; a record of another region is otherwise never a reason to remove anything.\n\nThe group's custody here becomes `standby` first, which is the run's point of no return, and nothing is undone after it; a run that stops is resumed. With `dry_run=true` the request answers 200 with the plan and starts nothing.\n\nAny member of the project the token is scoped to.",
        "operationId": "retireProtectionGroup",
        "parameters": [
          {
            "description": "The id of the protection group to retire.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` works out the plan the run would follow, answers it with 200 and starts nothing.",
            "in": "query",
            "name": "dry_run",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "reason": "the workload is back home"
              },
              "schema": {
                "$ref": "#/components/schemas/RetireReq"
              }
            }
          },
          "description": "Why. The body may be omitted.",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "dry_run": true,
                  "plan": {
                    "custody_epoch_to": 5,
                    "evidence": {
                      "cloud": "cloud-a",
                      "epoch": 5,
                      "recorded_at": "2026-10-03T09:00:00Z",
                      "run_id": "8b9c0d1e-2f3a-4b4c-8d5e-6f7a8b9c0d1e"
                    },
                    "members": [
                      {
                        "boot_order": 1,
                        "health": {
                          "type": ""
                        },
                        "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                        "point_id": "",
                        "running_instance_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
                      }
                    ],
                    "operation": "retire",
                    "recovery_point": "",
                    "rung": "cold",
                    "spread_seconds": 0
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DRPlanResponse"
                }
              }
            },
            "description": "OK.\n\nWith `dry_run=true`, the plan the run would follow. Nothing was started."
          },
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "job_id": "7a8b9c0d-1e2f-4a3b-9c4d-5e6f7a8b9c0d",
                  "run": {
                    "custody_epoch": {
                      "from": 4,
                      "to": 0
                    },
                    "deadline_at": "2026-10-03T22:00:00Z",
                    "exec_epoch": 0,
                    "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "id": "6f7a8b9c-0d1e-4f2a-8b3c-4d5e6f7a8b9c",
                    "job_id": "7a8b9c0d-1e2f-4a3b-9c4d-5e6f7a8b9c0d",
                    "measured": {},
                    "operation": "retire",
                    "prior_state": "degraded",
                    "reason": "the workload is back home",
                    "requested_by": "6c0f2d8e4a1b4c3d9e7f5a2b1c0d9e8f",
                    "resume_count": 0,
                    "rung": "cold",
                    "started_at": "2026-10-03T10:00:00Z",
                    "state": "pending",
                    "updated_at": "2026-10-03T10:00:00Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RunResponse"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not retire this group."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`group_busy`: A run is acting on the group.\n\n`custody_not_standby`: The workload runs here, because the group's custody here is `active`.\n\n`split_brain_risk`: The group is in `split_brain_risk`, which only a resync or an acknowledgement clears.\n\n`no_newer_custody_elsewhere`: The other region's record of recovering the group at the custody epoch held here or above has not been read here.\n\n`nothing_to_retire`: The group holds no server a recover built here."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here, or this cloud cannot delete servers or release volumes, which the detail names."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Retire a protection group's superseded recovery",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/runs": {
      "get": {
        "description": "Every release, recover, test, resync and retire of the group in this region, newest first, including tests its registration's cadence started. A run read here carries no `members`; GET /v1/protection-groups/{id}/runs/{run_id} adds them.\n\nAny member of the project the token is scoped to.",
        "operationId": "listDRRuns",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DRRunPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token has no member role in the group's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List a protection group's disaster recovery runs",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/runs/{run_id}": {
      "get": {
        "description": "One run, with each member's progress through it, and with its journal and its ledger on request: the steps it has taken and everything it created or meant to create.\n\nAny member of the project the token is scoped to.",
        "operationId": "getDRRun",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The run's id, from GET /v1/protection-groups/{id}/runs.",
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`actions` attaches the run's journal as `actions`, and `resources` its ledger as `resources`; both may be given, comma-separated. No other value is accepted.",
            "in": "query",
            "name": "include",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "confirmed_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                  "custody_epoch": {
                    "from": 3,
                    "to": 4
                  },
                  "deadline_at": "2026-10-03T22:05:00Z",
                  "exec_epoch": 1,
                  "finished_at": "2026-10-03T10:37:02Z",
                  "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                  "id": "9c0d1e2f-3a4b-4c5d-8e6f-7a8b9c0d1e2f",
                  "job_id": "4e5f6a7b-8c9d-4e0f-9a1b-3c4d5e6f7a8b",
                  "measured": {
                    "data_loss_seconds": 7488,
                    "recovery_point_at": "2026-10-03T08:00:12Z",
                    "recovery_point_newest_at": "2026-10-03T08:04:51Z",
                    "recovery_point_spread_seconds": 279,
                    "rto_seconds": 1874
                  },
                  "members": [
                    {
                      "addresses": [
                        "10.40.0.12"
                      ],
                      "boot_order": 1,
                      "booted_at": "2026-10-03T10:24:40Z",
                      "healthy_at": "2026-10-03T10:26:15Z",
                      "instance_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
                      "materialised_at": "2026-10-03T10:21:02Z",
                      "name": "db-01",
                      "point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                      "recovery_point_at": "2026-10-03T08:00:12Z",
                      "source_instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                      "state": "healthy"
                    },
                    {
                      "addresses": [
                        "10.40.0.27"
                      ],
                      "boot_order": 2,
                      "booted_at": "2026-10-03T10:28:03Z",
                      "healthy_at": "2026-10-03T10:36:14Z",
                      "instance_id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
                      "materialised_at": "2026-10-03T10:21:30Z",
                      "name": "app-01",
                      "point_id": "2a3b4c5d-6e7f-4809-9a1b-2c3d4e5f6a7b",
                      "recovery_point_at": "2026-10-03T08:04:51Z",
                      "source_instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                      "state": "healthy"
                    }
                  ],
                  "mode": "unplanned",
                  "operation": "recover",
                  "options": {
                    "max_point_age_seconds": 21600,
                    "recovery_point": "aligned"
                  },
                  "phase": "reprotect",
                  "ponr_at": "2026-10-03T10:21:37Z",
                  "prior_state": "ready",
                  "reason": "eu-ro-1 lost power; recovering payments in cloud-b",
                  "requested_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                  "resume_count": 0,
                  "rung": "cold",
                  "started_at": "2026-10-03T10:05:00Z",
                  "state": "succeeded",
                  "updated_at": "2026-10-03T10:37:02Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/DRRun"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token has no member role in the group's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project. Or the group has no run with this id."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a disaster recovery run",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/runs/{run_id}/abort": {
      "post": {
        "description": "Stops a run that is past its point of no return at its next step and leaves everything as it is: the run ends `halted` and the group `incomplete`. Nothing is removed. A halted run can be resumed. A run before its point of no return is cancelled instead. Asking twice changes nothing.\n\nOperator only: needs a system-scoped token.",
        "operationId": "abortDRRun",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The run's id.",
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "job_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
                  "run": {
                    "abort_requested_at": "2026-10-02T11:20:44Z",
                    "abort_requested_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                    "confirmed_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                    "custody_epoch": {
                      "from": 3,
                      "to": 4
                    },
                    "exec_epoch": 1,
                    "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "id": "8b9c0d1e-2f3a-4b4c-8d5e-6f7a8b9c0d1e",
                    "job_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
                    "measured": {},
                    "mode": "unplanned",
                    "operation": "recover",
                    "phase": "boot",
                    "ponr_at": "2026-10-02T11:12:09Z",
                    "prior_state": "ready",
                    "requested_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                    "resume_count": 0,
                    "rung": "warm",
                    "started_at": "2026-10-02T11:05:00Z",
                    "state": "running",
                    "updated_at": "2026-10-02T11:20:44Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RunResponse"
                }
              }
            },
            "description": "Accepted"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not an operator token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists. Or the group has no run with this id."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The run has not reached its point of no return, or has already ended."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Abort a disaster recovery run",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/protection-groups/{id}/runs/{run_id}/cancel": {
      "post": {
        "description": "Asks the run to stop. It is honoured at the run's next step, or at once if the run is waiting: the run removes what it created, returns the group to the state it was in, and ends `cancelled`. A run past its point of no return cannot be cancelled. Asking twice changes nothing.\n\nAny member of the project the token is scoped to.",
        "operationId": "cancelDRRun",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The run's id.",
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "job_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
                  "run": {
                    "cancel_requested_at": "2026-10-02T10:04:31Z",
                    "cancel_requested_by": "6c0f2d8e4a1b4c3d9e7f5a2b1c0d9e8f",
                    "custody_epoch": {
                      "from": 1,
                      "to": 0
                    },
                    "exec_epoch": 2,
                    "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
                    "job_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
                    "measured": {},
                    "operation": "test",
                    "phase": "bubble",
                    "prior_state": "protected",
                    "requested_by": "6c0f2d8e4a1b4c3d9e7f5a2b1c0d9e8f",
                    "resume_count": 0,
                    "started_at": "2026-10-02T10:00:00Z",
                    "state": "waiting",
                    "updated_at": "2026-10-02T10:04:31Z",
                    "waiting_reason": "waiting for the isolated network to come up"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RunResponse"
                }
              }
            },
            "description": "Accepted"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not cancel runs of this group."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project. Or the group has no run with this id."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`past_point_of_no_return`: The run passed its point of no return, or halted after it.\n\n`conflict`: The run has already ended."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Cancel a disaster recovery run",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/runs/{run_id}/resume": {
      "post": {
        "description": "Picks a halted run up again as the same run, with the same journal and ledger: steps already done are skipped, and the one that failed is looked at again before it is retried. A resumed recover first re-plans what has not run yet from the registration's mapping profile and accepted gaps, the target project and the probe configuration as they are now, so a cause fixed since the halt is honoured; boot, promote and health waits start their bound again, a member's server that failed to build is replaced, and a health gate boots a fresh probe host. The group must still be `incomplete` with no other run acting on it. Refused with `dr_not_enabled` when disaster recovery is not enabled on this service.\n\nAny member of the project the token is scoped to.",
        "operationId": "resumeDRRun",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The run's id.",
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "job_id": "3d4e5f6a-7b8c-4d9e-8f0a-2b3c4d5e6f7a",
                  "run": {
                    "confirmed_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                    "custody_epoch": {
                      "from": 3,
                      "to": 4
                    },
                    "exec_epoch": 1,
                    "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "id": "8b9c0d1e-2f3a-4b4c-8d5e-6f7a8b9c0d1e",
                    "job_id": "3d4e5f6a-7b8c-4d9e-8f0a-2b3c4d5e6f7a",
                    "measured": {},
                    "mode": "unplanned",
                    "operation": "recover",
                    "phase": "boot",
                    "ponr_at": "2026-10-02T11:12:09Z",
                    "prior_state": "ready",
                    "requested_by": "b2c3d4e5f6a7081920a1b2c3d4e5f607",
                    "resume_count": 1,
                    "rung": "warm",
                    "started_at": "2026-10-02T11:05:00Z",
                    "state": "pending",
                    "updated_at": "2026-10-02T12:01:17Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RunResponse"
                }
              }
            },
            "description": "Accepted"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not resume runs of this group."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project. Or the group has no run with this id."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`run_not_resumable`: The run is not halted.\n\n`group_busy`: Another run is acting on the group, or the group is no longer `incomplete`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled on this service."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Resume a halted disaster recovery run",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/sla": {
      "get": {
        "description": "What the group has actually achieved over the last 30 days, against the targets its policy's recovery section states: the recovery point its member servers' backups give, the recovery times its tests measured, replication lag, the backup success rate and since when it has been immutably protected. On a recovery the recovery times are its own tests'; at the group's home they are the results the recovery region writes back. A figure with no measurement yet is listed under `unavailable` with the reason.\n\nAny member of the project the token is scoped to.",
        "operationId": "getProtectionGroupSLA",
        "parameters": [
          {
            "description": "The protection group's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlaResponse"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token has no member role in the group's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a protection group's measured protection",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/protection-groups/{id}/split-brain/acknowledge": {
      "post": {
        "description": "Clears `split_brain_risk` here by saying the other region's copy is the one that was discarded and this one keeps running. Nothing else clears it but a resync: no record from the other region does, however many times it resyncs or reports. The acknowledgement takes custody here at an epoch above the other region's, and records that where the other region reads it, as a recovery here; the other region then sees a recovery above what it holds and refuses to run its own copy until it is resynced. Recorded in the audit log with who gave it and why. Operator only, and only with `confirm_other_region_discarded`.\n\nOperator only: needs a system-scoped token.",
        "operationId": "acknowledgeSplitBrain",
        "parameters": [
          {
            "description": "The id of the protection group in `split_brain_risk`.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "confirm_other_region_discarded": true,
                "reason": "cloud-b recovered from a stale copy during the network partition"
              },
              "schema": {
                "$ref": "#/components/schemas/AcknowledgeReq"
              }
            }
          },
          "description": "The acknowledgement and why.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "custody_epoch": 5,
                  "group": {
                    "armed": true,
                    "created_at": "2026-09-01T10:00:00Z",
                    "custody": "active",
                    "custody_epoch": 5,
                    "doc_version": 7,
                    "domain_id": "default",
                    "group_uid": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "members": [
                      {
                        "boot_order": 1,
                        "health": {
                          "port": 5432,
                          "type": "tcp"
                        },
                        "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60"
                      }
                    ],
                    "name": "payments-prod",
                    "network_map": null,
                    "policy_id": "2a1b3c4d-5e6f-4708-8991-aabbccddeeff",
                    "project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                    "role": "primary",
                    "site_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
                    "state": "protected",
                    "state_reason": "acknowledged by b2c3d4e5f6a7081920a1b2c3d4e5f607 at epoch 5: cloud-b recovered from a stale copy during the network partition",
                    "updated_at": "2026-10-03T10:00:00Z"
                  },
                  "record_run_id": "0d1e2f3a-4b5c-4d6e-9f7a-8b9c0d1e2f3a"
                },
                "schema": {
                  "$ref": "#/components/schemas/AcknowledgeResponse"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not an operator token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`split_brain_risk`: The group is not in `split_brain_risk`, a run is acting on it, or it changed while the acknowledgement was being recorded."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`acknowledge_unconfirmed`: `confirm_other_region_discarded` is missing or false."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here, or this cloud cannot write where the other region reads."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Acknowledge a split brain",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/protection-groups/{id}/test": {
      "post": {
        "description": "Proves that the group could be recovered here, in the region that holds its standby copy, without touching the group, its project or the workload at home. Under a test policy, the run builds an isolated network in the policy's operator project and proves it has no way out: no router, gateway or name servers, and, from a jump host inside it, none of the policy's isolation canaries reachable. It then restores copies of the members from their newest backups adopted here, or clones their standbys, onto that network, boots them tier by tier in boot order, and checks each tier's health from the jump host before the next. The time from admission to the last member healthy is the run's `measured.rto_seconds`, which the group's measured protection reports. The result is recorded in the repository for the group's home to read, and everything the test built is torn down; what it built also expires at the policy's `max_duration` even if the run is gone. The group is `testing` while the run is active and returns to the state it was in when it ends; its custody never changes.\n\nWith `dry_run=true` the request answers 200 with the plan the run would follow and starts nothing. Otherwise it answers 202 with the admitted run; follow it at GET /v1/protection-groups/{id}/runs/{run_id}. Everything that would refuse the run is checked before it is admitted, and again by the run itself before it acts. A refusal about members names each in `fields` as `members.<instance id>`. A registration with a test cadence is also tested on its own when a test falls due.\n\nA test of a group at its home and of the `hot` rung are refused in this release.\n\nAny member of the project the token is scoped to.",
        "operationId": "testProtectionGroup",
        "parameters": [
          {
            "description": "The id of the protection group to test.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` works out the plan the run would follow, answers it with 200 and starts nothing: no run or job is created and the group keeps its state. Every refusal is answered as it would be without it.",
            "in": "query",
            "name": "dry_run",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "bubble_policy_id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
                "reason": "quarterly recovery drill"
              },
              "schema": {
                "$ref": "#/components/schemas/TestReq"
              }
            }
          },
          "description": "The test policy and why. The body may be omitted.",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "dry_run": true,
                  "plan": {
                    "bubble_policy_id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
                    "bubble_project": "9a8b7c6d5e4f40312a1b0c9d8e7f6a5b",
                    "members": [
                      {
                        "applied_snap": "applied-1f2e3d4c",
                        "boot_order": 1,
                        "health": {
                          "port": 5432,
                          "retries": 10,
                          "timeout": 10000000000,
                          "type": "tcp"
                        },
                        "instance_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                        "name": "db-01",
                        "point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                        "recovery_point_at": "2026-10-03T09:00:12Z",
                        "standby_id": "7b8c9d0e-1f2a-4b3c-9d4e-5f6a7b8c9d0e",
                        "standby_pool": "standby"
                      },
                      {
                        "applied_snap": "applied-2a3b4c5d",
                        "boot_order": 2,
                        "health": {
                          "expect": 200,
                          "path": "/healthz",
                          "port": 8080,
                          "retries": 10,
                          "timeout": 15000000000,
                          "type": "http"
                        },
                        "instance_id": "c9f0f895-fb98-4b91-9ad2-5a3c1f0e7b21",
                        "name": "app-01",
                        "point_id": "2a3b4c5d-6e7f-4809-9a1b-2c3d4e5f6a7b",
                        "recovery_point_at": "2026-10-03T09:02:40Z",
                        "standby_id": "8c9d0e1f-2a3b-4c4d-8e5f-6a7b8c9d0e1f",
                        "standby_pool": "standby"
                      }
                    ],
                    "newest_at": "2026-10-03T09:02:40Z",
                    "oldest_at": "2026-10-03T09:00:12Z",
                    "operation": "test",
                    "probe_flavor": "m1.small",
                    "probe_image": "c0a8f1e2-3b4d-4c5e-9f60-7182a3b4c5d6",
                    "recovery_point": "newest",
                    "rung": "warm",
                    "spread_seconds": 148
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DRPlanResponse"
                }
              }
            },
            "description": "OK.\n\nWith `dry_run=true`, the plan the run would follow. Nothing was started."
          },
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "job_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
                  "run": {
                    "bubble_policy_id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
                    "custody_epoch": {
                      "from": 3,
                      "to": 0
                    },
                    "deadline_at": "2026-10-03T12:00:00Z",
                    "exec_epoch": 0,
                    "group_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                    "id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
                    "job_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
                    "measured": {},
                    "operation": "test",
                    "prior_state": "ready",
                    "reason": "quarterly recovery drill",
                    "requested_by": "6c0f2d8e4a1b4c3d9e7f5a2b1c0d9e8f",
                    "resume_count": 0,
                    "rung": "warm",
                    "started_at": "2026-10-03T10:00:00Z",
                    "state": "pending",
                    "updated_at": "2026-10-03T10:00:00Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RunResponse"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not test this group."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No group with this id exists in the token's project.\n\n`group_document_not_found`: The group's repository no longer shows the document its home writes."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`group_busy`: A run is acting on the group.\n\n`custody_not_standby`: This region does not hold the group's standby copy, because its custody here is `active` or `released`.\n\n`split_brain_risk`: The group is in `split_brain_risk`, which only a resync or an acknowledgement clears."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `bubble_policy_id` is not a UUID.\n\n`bubble_policy_required`: Neither the request nor the group's registration names a test policy, or the one named does not exist.\n\n`bubble_policy_has_no_project`: The test policy names no operator project to build in.\n\n`bubble_policy_invalid`: The test policy cannot build an isolated network as it stands, such as one with no address range or no maximum duration.\n\n`check_not_permitted`: A member's health check type is not among the test policy's `permitted_checks`.\n\n`no_probe_origin`: Members declare health checks or the policy names isolation canaries, and there is nothing to run them from: the policy is strict with no jump host, or neither the policy nor this service names a jump host image.\n\n`bubble_project_unreachable`: The service cannot act in the test policy's project. An operator grants it a role there.\n\n`group_has_no_policy`: The group names no protection policy.\n\n`policy_has_no_recovery`: The group's policy states no recovery section.\n\n`member_not_mirrored`: For a `hot` group, a member's disk is not a mirrored volume here.\n\n`image_not_found_by_global_id`: For a `hot` group, no image here carries a member's disk's mirror global id.\n\n`member_never_synced`: No standby holds a member of a `warm` group yet, or a mirrored image of a `hot` group holds no complete mirror snapshot to clone; one still syncing is never cloned.\n\n`member_has_no_recoverable_state`: No backup of a member has been adopted here, or the group has no member.\n\n`domain_key_missing`: A member's backups are encrypted under a dedup domain whose key this cloud lacks.\n\n`member_not_ready`: A member's readiness reports something else that refuses a recovery, such as `binding_missing`; `fields` gives it as `<issue code>: <detail>`.\n\n`standby_has_no_applied_snapshot`: A member's standby holds no snapshot taken when an update finished.\n\n`partial_coverage_unaccepted`: A member's newest backup does not hold every disk of the server, and the group's registration does not accept `partial_coverage`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`dr_not_enabled`: Disaster recovery is not enabled here. Or the group is at its home, which this release does not test. Or this cloud lacks something a test needs, which the detail names."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Test a protection group",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/quotas": {
      "get": {
        "description": "Each limit, where it comes from and how much of it is in use. Also whether new protection is being admitted, and when locked backups release their space.\n\nAny reader of the project the token is scoped to.",
        "operationId": "getQuotas",
        "parameters": [
          {
            "description": "The project to answer for. Defaults to the token's project, and is required for a token not scoped to one. Naming another project needs permission to read other projects.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Operator only. The project's domain, so its domain limits apply when the service has not learned the domain from the project's plans and backups. Ignored for a token that may not read that domain's projects. GET /v1/quotas/domains/{id} answers for a whole domain.",
            "in": "query",
            "name": "domain_id",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuotaView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `quota:read` permission. Or `project_id` names another project and the token may not read other projects."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `project_id` is missing and the token is not scoped to a project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show the project's quota",
        "tags": [
          "Quotas"
        ],
        "x-access": "reader"
      }
    },
    "/v1/quotas/defaults": {
      "delete": {
        "description": "Projects with no limit of their own or of their domain become unlimited.\n\nOperator only: needs a system-scoped token.",
        "operationId": "clearQuotaDefaults",
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `quota:write` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No defaults are set."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Remove the cloud's default quota",
        "tags": [
          "Quotas"
        ],
        "x-access": "operator"
      },
      "get": {
        "description": "Operator only: needs a system-scoped token.",
        "operationId": "getQuotaDefaults",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "resources": 100,
                  "scope": "defaults",
                  "source_bytes": 10995116277760,
                  "stored_bytes": -1,
                  "updated_at": "2026-09-01T07:55:03Z",
                  "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                },
                "schema": {
                  "$ref": "#/components/schemas/Quota"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `quota:write` permission."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show the cloud's default quota",
        "tags": [
          "Quotas"
        ],
        "x-access": "operator"
      },
      "put": {
        "description": "Applies to every project with no limit of its own or of its domain. Name at least one of `source_bytes`, `resources` and `stored_bytes`. `-1` is unlimited.\n\nOperator only: needs a system-scoped token.",
        "operationId": "setQuotaDefaults",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "resources": 100,
                "source_bytes": 10995116277760,
                "stored_bytes": -1
              },
              "schema": {
                "$ref": "#/components/schemas/PutQuotaReq"
              }
            }
          },
          "description": "The cloud's default limits, for every project with no limit of its own or of its domain. They replace the current defaults; a limit left out is unlimited unless a domain or project sets it.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "resources": 100,
                  "scope": "defaults",
                  "source_bytes": 10995116277760,
                  "stored_bytes": -1,
                  "updated_at": "2026-09-01T07:55:03Z",
                  "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                },
                "schema": {
                  "$ref": "#/components/schemas/Quota"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `quota:write` permission."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A limit is below -1, or the body names no limit."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Set the cloud's default quota",
        "tags": [
          "Quotas"
        ],
        "x-access": "operator"
      }
    },
    "/v1/quotas/domains/{id}": {
      "delete": {
        "description": "Its projects go back to the cloud's defaults.\n\nOperator only: needs a system-scoped token.",
        "operationId": "clearDomainQuota",
        "parameters": [
          {
            "description": "The Keystone domain id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `quota:write` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The domain has no limits of its own."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Remove a domain's quota",
        "tags": [
          "Quotas"
        ],
        "x-access": "operator"
      },
      "get": {
        "description": "The limits that apply to each project of the domain that has none of its own, and where each of its projects with a plan or a backup stands.\n\nOperator only: needs a system-scoped token.",
        "operationId": "getDomainQuota",
        "parameters": [
          {
            "description": "The Keystone domain id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "domain_id": "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a",
                  "projects": [
                    {
                      "admission": {
                        "open": true
                      },
                      "domain_id": "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a",
                      "locked_bytes": 0,
                      "points": {
                        "deletable": 380,
                        "held": 0,
                        "locked": 0,
                        "mandatory": 32,
                        "project_held": false,
                        "total": 412
                      },
                      "project_id": "9a7c3e1f5b2d4a6c8e0f1a3b5c7d9e2f",
                      "reclaimable_bytes": 483183820800,
                      "resources": {
                        "in_use": 14,
                        "limit": 100,
                        "over": false,
                        "source": "defaults"
                      },
                      "rows": {
                        "defaults": {
                          "resources": 100,
                          "scope": "defaults",
                          "stored_bytes": -1,
                          "updated_at": "2026-09-01T07:55:03Z",
                          "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                        },
                        "domain": {
                          "scope": "domain",
                          "source_bytes": 54975581388800,
                          "target_id": "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a",
                          "updated_at": "2026-09-02T12:20:41Z",
                          "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                        }
                      },
                      "sentence": "Stored 450 GiB, measured 3 hours ago. 32 are kept by a mandatory policy. 380 backups can be deleted now.",
                      "source_bytes": {
                        "in_use": 1288490188800,
                        "limit": 54975581388800,
                        "over": false,
                        "source": "domain"
                      },
                      "stored_bytes": {
                        "computed_at": "2026-10-02T05:10:00Z",
                        "in_use": 483183820800,
                        "limit": -1,
                        "over": false,
                        "source": "defaults"
                      }
                    }
                  ],
                  "resources": {
                    "limit": 100,
                    "over": false,
                    "source": "defaults"
                  },
                  "rows": {
                    "defaults": {
                      "resources": 100,
                      "scope": "defaults",
                      "stored_bytes": -1,
                      "updated_at": "2026-09-01T07:55:03Z",
                      "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                    },
                    "domain": {
                      "scope": "domain",
                      "source_bytes": 54975581388800,
                      "target_id": "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a",
                      "updated_at": "2026-09-02T12:20:41Z",
                      "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                    }
                  },
                  "source_bytes": {
                    "limit": 54975581388800,
                    "over": false,
                    "source": "domain"
                  },
                  "stored_bytes": {
                    "limit": -1,
                    "over": false,
                    "source": "defaults"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/QuotaDomainView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `quota:read` permission, or may not read that domain's projects."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a domain's quota",
        "tags": [
          "Quotas"
        ],
        "x-access": "operator"
      },
      "put": {
        "description": "Limits for each project of the domain that has none of its own. Each limit holds per project, not over their sum. A limit left out comes from the cloud's defaults. `-1` is unlimited.\n\nOperator only: needs a system-scoped token.",
        "operationId": "setDomainQuota",
        "parameters": [
          {
            "description": "The Keystone domain id. It is not checked against Keystone.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "resources": 500,
                "source_bytes": 54975581388800
              },
              "schema": {
                "$ref": "#/components/schemas/PutQuotaReq"
              }
            }
          },
          "description": "The domain's limits, applied to each of its projects that has none of its own, per project rather than over their sum. They replace the domain's current limits; a limit left out comes from the cloud's defaults.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "resources": 500,
                  "scope": "domain",
                  "source_bytes": 54975581388800,
                  "target_id": "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a",
                  "updated_at": "2026-09-02T12:20:41Z",
                  "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                },
                "schema": {
                  "$ref": "#/components/schemas/Quota"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `quota:write` permission."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: The id is blank. Or a limit is below -1. Or the body names no limit."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Set a domain's quota",
        "tags": [
          "Quotas"
        ],
        "x-access": "operator"
      }
    },
    "/v1/quotas/projects/{id}": {
      "delete": {
        "description": "The project goes back to its domain's limits or the cloud's defaults.\n\nOperator only: needs a system-scoped token.",
        "operationId": "clearProjectQuota",
        "parameters": [
          {
            "description": "The Keystone project id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `quota:write` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The project has no limits of its own."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Remove a project's quota",
        "tags": [
          "Quotas"
        ],
        "x-access": "operator"
      },
      "put": {
        "description": "A limit left out comes from the project's domain or the cloud's defaults. `-1` is unlimited. Lowering a limit below what is in use stops new protection, never scheduled backups.\n\nOperator only: needs a system-scoped token.",
        "operationId": "setProjectQuota",
        "parameters": [
          {
            "description": "The Keystone project id. It is checked against Keystone when the service may read projects there; when it may not, the id is taken as given.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "source_bytes": 5497558138880,
                "stored_bytes": 2199023255552
              },
              "schema": {
                "$ref": "#/components/schemas/PutQuotaReq"
              }
            }
          },
          "description": "The project's limits. They replace its current limits; a limit left out comes from its domain or the cloud's defaults. A limit lowered below what is in use stops new protection, never scheduled backups.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "admission": {
                    "open": true
                  },
                  "domain_id": "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a",
                  "locked_bytes": 161061273600,
                  "points": {
                    "deletable": 380,
                    "held": 0,
                    "locked": 96,
                    "locked_from": "2026-10-09T02:14:31Z",
                    "locked_until": "2027-04-01T02:09:12Z",
                    "mandatory": 32,
                    "next_release_at": "2026-10-09T00:00:00Z",
                    "project_held": false,
                    "releasing": 3,
                    "releasing_at": "2026-10-05T11:20:00Z",
                    "total": 412
                  },
                  "project_id": "9a7c3e1f5b2d4a6c8e0f1a3b5c7d9e2f",
                  "reclaimable_bytes": 322122547200,
                  "release_schedule": [
                    {
                      "bytes": 21474836480,
                      "month": "2026-10-01T00:00:00Z"
                    },
                    {
                      "bytes": 32212254720,
                      "month": "2026-11-01T00:00:00Z"
                    },
                    {
                      "bytes": 107374182400,
                      "month": "2027-04-01T00:00:00Z"
                    }
                  ],
                  "resources": {
                    "in_use": 14,
                    "limit": 500,
                    "over": false,
                    "source": "domain"
                  },
                  "rows": {
                    "defaults": {
                      "resources": 100,
                      "scope": "defaults",
                      "source_bytes": 10995116277760,
                      "stored_bytes": -1,
                      "updated_at": "2026-09-01T07:55:03Z",
                      "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                    },
                    "domain": {
                      "resources": 500,
                      "scope": "domain",
                      "source_bytes": 54975581388800,
                      "target_id": "2f1c8e4a9b7d4c6e8f0a1b2c3d4e5f6a",
                      "updated_at": "2026-09-02T12:20:41Z",
                      "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                    },
                    "project": {
                      "scope": "project",
                      "source_bytes": 5497558138880,
                      "stored_bytes": 2199023255552,
                      "target_id": "9a7c3e1f5b2d4a6c8e0f1a3b5c7d9e2f",
                      "updated_at": "2026-10-02T08:01:19Z",
                      "updated_by": "e4b2d6f8a1c3e5b7d9f1a3c5e7b9d1f3"
                    }
                  },
                  "sentence": "Stored 450 GiB of 2.0 TiB, measured 3 hours ago. 96 of 412 backups are locked; the earliest lock ends 9 October 2026, the last 1 April 2027. Data a newer backup still shares is locked again by that backup, so a lock ending frees only what nothing newer references. 32 are kept by a mandatory policy. 380 backups can be deleted now.",
                  "source_bytes": {
                    "in_use": 1288490188800,
                    "limit": 5497558138880,
                    "over": false,
                    "source": "project"
                  },
                  "stored_bytes": {
                    "computed_at": "2026-10-02T05:10:00Z",
                    "in_use": 483183820800,
                    "limit": 2199023255552,
                    "over": false,
                    "source": "project"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/QuotaView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `quota:write` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: Keystone has no project with this id."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: The id is blank. Or a limit is below -1. Or the body names no limit."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Set a project's quota",
        "tags": [
          "Quotas"
        ],
        "x-access": "operator"
      }
    },
    "/v1/recovery-bindings": {
      "get": {
        "description": "Operator only: needs a system-scoped token.",
        "operationId": "listRecoveryBindings",
        "parameters": [
          {
            "description": "Only bindings that read from this repository.",
            "in": "query",
            "name": "repository_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only bindings for resources the cloud with this name backed up.",
            "in": "query",
            "name": "origin_cloud",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only bindings into this project.",
            "in": "query",
            "name": "target_project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecoveryBindingPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `repository_id` is not a UUID."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List recovery bindings",
        "tags": [
          "Recovery"
        ],
        "x-access": "operator"
      },
      "post": {
        "description": "Declares that a resource another cloud backed up into a shared repository belongs to a project here. Its backups then appear in that project and can be restored there, and new ones are picked up as they arrive. Naming a standby pool also keeps a warm copy of it. A resource can be bound once.\n\nOperator only: needs a system-scoped token.",
        "operationId": "createRecoveryBinding",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "origin_cloud": "eu-ro-1",
                "repository_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
                "source_project": "7e6d5c4b3a291807f6e5d4c3b2a19080",
                "source_resource_id": "d3b07384-d113-4ec4-9a0e-6c1f2b3a4d5e",
                "standby_pool": "standby",
                "standby_prefix": "acc-dr-",
                "target_project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateBindingReq"
              }
            }
          },
          "description": "The resource in the other cloud, the repository its backups are in, and the project here they belong to.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "asserted_by": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                  "created_at": "2026-09-20T10:55:00Z",
                  "id": "8b9c0d1e-2f3a-4b5c-8d6e-7f8091a2b3c4",
                  "origin_cloud": "eu-ro-1",
                  "points_adopted": 0,
                  "repository_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
                  "source_project": "7e6d5c4b3a291807f6e5d4c3b2a19080",
                  "source_resource_id": "d3b07384-d113-4ec4-9a0e-6c1f2b3a4d5e",
                  "standby_pool": "standby",
                  "standby_prefix": "acc-dr-",
                  "target_domain_id": "default",
                  "target_project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                  "updated_at": "2026-09-20T10:55:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/RecoveryBinding"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: `repository_id` names no registered repository."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`tier_unavailable`: The repository is an rbd tier, which holds no backup records.\n\n`conflict`: This resource from this cloud is already bound. Or `target_project_id` names a project that does not exist here or is disabled."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `origin_cloud`, `source_resource_id` or `target_project_id` is missing. `repository_id` is not a UUID. `standby_prefix` is given without `standby_pool`. `standby_site_id` is not a UUID. `target_domain_id` does not match the target project's domain, or is missing when the project cannot be looked up."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`service_unavailable`: This service does not hold recovery bindings."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Bind a resource from another cloud to a project",
        "tags": [
          "Recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/recovery-bindings/{id}": {
      "delete": {
        "description": "Stops picking up new backups of the resource. Backups already brought in stay restorable, and a standby the binding created is left in place.\n\nOperator only: needs a system-scoped token.",
        "operationId": "deleteRecoveryBinding",
        "parameters": [
          {
            "description": "The recovery binding's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The id is not a UUID."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No binding with this id exists."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Delete a recovery binding",
        "tags": [
          "Recovery"
        ],
        "x-access": "operator"
      },
      "get": {
        "description": "Operator only: needs a system-scoped token.",
        "operationId": "getRecoveryBinding",
        "parameters": [
          {
            "description": "The recovery binding's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "asserted_by": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                  "created_at": "2026-09-20T10:55:00Z",
                  "id": "8b9c0d1e-2f3a-4b5c-8d6e-7f8091a2b3c4",
                  "last_adopted_at": "2026-10-02T06:01:12Z",
                  "origin_cloud": "eu-ro-1",
                  "points_adopted": 12,
                  "repository_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
                  "source_project": "7e6d5c4b3a291807f6e5d4c3b2a19080",
                  "source_resource_id": "d3b07384-d113-4ec4-9a0e-6c1f2b3a4d5e",
                  "standby_id": "6c5b4a39-2817-4f06-a5e4-d3c2b1a09f8e",
                  "standby_pool": "standby",
                  "standby_prefix": "acc-dr-",
                  "target_domain_id": "default",
                  "target_project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                  "updated_at": "2026-10-02T06:01:12Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/RecoveryBinding"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The id is not a UUID."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No binding with this id exists."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a recovery binding",
        "tags": [
          "Recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/recovery-bindings/{id}/dry-run": {
      "post": {
        "description": "Works out how a bound resource's backup would be rebuilt here: the flavor, networks, volume types and other settings it would get, and what cannot be matched. Nothing is created. It uses the newest backup unless `point_id` names one, and a stored recovery profile or one given inline. Items listed in `confirm_unresolved` are accepted as they are.\n\nOperator only: needs a system-scoped token.",
        "operationId": "dryRunRecoveryBinding",
        "parameters": [
          {
            "description": "The recovery binding's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "confirm_unresolved": [
                  "instance_availability_zone:nova"
                ],
                "profile": "dr-default"
              },
              "schema": {
                "$ref": "#/components/schemas/BindingDryRunReq"
              }
            }
          },
          "description": "Optional. Which adopted backup to resolve, with which profile, and which unresolved elements to accept.",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "binding_id": "8b9c0d1e-2f3a-4b5c-8d6e-7f8091a2b3c4",
                  "committable": true,
                  "point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                  "repository": "ovh-bucharest",
                  "resolution": {
                    "committable": false,
                    "create_summary": {
                      "floating_ips": 0,
                      "instances": 1,
                      "keypairs": 0,
                      "ports": 1,
                      "security_groups": 0,
                      "storage_gb": 80,
                      "volumes": 1
                    },
                    "creates": [
                      {
                        "detail": "4 vCPU / 8192 MB RAM",
                        "kind": "instance",
                        "name": "payments-db-01",
                        "target": "m1.large"
                      },
                      {
                        "detail": "bootable",
                        "kind": "volume",
                        "name": "payments-db-01-root",
                        "size_gb": 80,
                        "target": "ceph-ssd"
                      },
                      {
                        "detail": "a new fixed address is allocated; the source's is not preserved",
                        "kind": "port",
                        "name": "5d6e7f80-91a2-4b3c-8d4e-5f60718293a4",
                        "target": "dr-net"
                      }
                    ],
                    "differs": [
                      {
                        "action": "reuse",
                        "element": "security_group",
                        "reason": "an existing group of this name is reused as it stands; its rules are not changed to match the source",
                        "source": "default",
                        "target": "default"
                      },
                      {
                        "action": "default",
                        "element": "subnet",
                        "reason": "no subnet map entry; the target cloud allocates from the mapped network",
                        "source": "prod-subnet"
                      },
                      {
                        "action": "reallocate",
                        "element": "fixed_ip",
                        "reason": "a fixed address from the source subnet is not allocatable in the target; the port gets a new address",
                        "source": "10.0.0.14"
                      },
                      {
                        "action": "drop",
                        "element": "port_mac",
                        "reason": "MAC addresses are allocated by the target cloud; a guest pinning its interface by MAC will not see the same one",
                        "source": ""
                      }
                    ],
                    "plan": {
                      "flavor_id": "a3f1c2d4-5e6b-47a8-9c0d-1e2f3a4b5c6d",
                      "flavor_name": "m1.large",
                      "keypair_name": "ops",
                      "ports": [
                        {
                          "drop_fixed_ip": true,
                          "network_id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
                          "source_port_id": "5d6e7f80-91a2-4b3c-8d4e-5f60718293a4"
                        }
                      ],
                      "security_groups": {
                        "groups": [
                          {
                            "action": "reuse",
                            "source_id": "0a1b2c3d-4e5f-4061-8728-394a5b6c7d8e",
                            "source_name": "default",
                            "target_id": "f0e1d2c3-b4a5-4697-8879-6a5b4c3d2e1f"
                          }
                        ]
                      },
                      "target_project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                      "volumes": [
                        {
                          "source_volume_id": "e5f6a7b8-c9d0-4e1f-a2b3-c4d5e6f7a8b9",
                          "volume_type": "ceph-ssd"
                        }
                      ]
                    },
                    "point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                    "profile_id": "4d5e6f70-8192-4a3b-9c4d-5e6f708192a3",
                    "resolved": [
                      {
                        "action": "match_by_name",
                        "element": "security_group",
                        "source": "default",
                        "target": "default"
                      },
                      {
                        "action": "match_by_name",
                        "element": "flavor",
                        "source": "m1.large",
                        "target": "m1.large"
                      },
                      {
                        "action": "match_by_name",
                        "element": "keypair",
                        "source": "ops",
                        "target": "ops"
                      },
                      {
                        "action": "verbatim",
                        "element": "config_drive",
                        "source": "false"
                      },
                      {
                        "action": "explicit_map",
                        "element": "network",
                        "source": "prod-net",
                        "target": "dr-net"
                      },
                      {
                        "action": "match_by_name",
                        "element": "volume_type",
                        "source": "ceph-ssd",
                        "target": "ceph-ssd"
                      }
                    ],
                    "unresolved": [
                      {
                        "element": "instance_availability_zone",
                        "reason": "no availability zone map entry for this zone in the profile",
                        "source": "nova"
                      }
                    ]
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/BindingDryRunResult"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No binding with this id exists. Or `point_id` is not one of the binding's adopted backups. Or `profile` names no stored profile. Or the repository holds no record of the backup."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The binding has adopted no backups yet.\n\n`domain_key_missing`: The encryption key for the backup is not present here, or the one present did not write it.\n\n`mapping_unresolved`: No inline profile was given, and more than one cloud wrote backups to this repository under the backup's cloud name.\n\n`tier_unavailable`: The backup's repository is not a chunk repository."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: Both `profile` and `mapping_profile` are given."
          },
          "502": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Gateway.\n\n`service_unavailable`: This cloud's flavors, networks or volume types could not be read. Or the repository could not be opened."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`kms_unavailable`: The backup is encrypted and this service has no key manager to unwrap its key with.\n\n`service_unavailable`: This service has no OpenStack access to read this cloud's inventory with."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Check a recovery before running it",
        "tags": [
          "Recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/recovery-profiles": {
      "get": {
        "description": "Operator only: needs a system-scoped token.",
        "operationId": "listRecoveryProfiles",
        "parameters": [
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecoveryProfilePage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List recovery profiles",
        "tags": [
          "Recovery"
        ],
        "x-access": "operator"
      },
      "post": {
        "description": "A named, reusable translation from another cloud's flavors, networks, subnets, volume types, availability zones, security groups and key pairs to this cloud's. Restores and dry runs of that cloud's backups use it by name.\n\nOperator only: needs a system-scoped token.",
        "operationId": "createRecoveryProfile",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "flavor_fallback_by_spec": true,
                "flavor_map": {
                  "m1.xlarge": "m2.xlarge"
                },
                "floating_pool": "public",
                "name": "dr-default",
                "network_map": {
                  "prod-net": "dr-net"
                },
                "origin_cloud": "eu-ro-1",
                "recreate_security_groups": true,
                "volume_type_map": {
                  "ceph-hdd": "ceph-ssd"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/ProfileReq"
              }
            }
          },
          "description": "The profile's name, the cloud it translates from, and its maps and switches.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-20T10:40:00Z",
                  "flavor_fallback_by_spec": true,
                  "flavor_map": {
                    "m1.xlarge": "m2.xlarge"
                  },
                  "floating_pool": "public",
                  "id": "4d5e6f70-8192-4a3b-9c4d-5e6f708192a3",
                  "import_keypair": false,
                  "name": "dr-default",
                  "network_map": {
                    "prod-net": "dr-net"
                  },
                  "origin_cloud": "eu-ro-1",
                  "recreate_security_groups": true,
                  "volume_type_map": {
                    "ceph-hdd": "ceph-ssd"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RecoveryProfile"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: A profile with this name already exists for this `origin_cloud`."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `name` is missing."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`service_unavailable`: This service does not hold recovery profiles."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Create a recovery profile",
        "tags": [
          "Recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/recovery-profiles/{id}": {
      "delete": {
        "description": "Operator only: needs a system-scoped token.",
        "operationId": "deleteRecoveryProfile",
        "parameters": [
          {
            "description": "The recovery profile's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The id is not a UUID."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No profile with this id exists."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`service_unavailable`: This service does not hold recovery profiles."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Delete a recovery profile",
        "tags": [
          "Recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/repositories": {
      "get": {
        "description": "Operator only: needs a system-scoped token.",
        "operationId": "listRepositories",
        "parameters": [
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RepositoryViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles grant neither `repository:usage` nor `peer:repository:read`, which another cloud's service reads repositories with."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List repositories",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      },
      "post": {
        "description": "Registers storage that backups are written to. A chunk repository keeps deduplicated backups through the `s3`, `rados` or `posix` driver. An rbd tier keeps recent backups as Ceph images that restores of new volumes and servers are cloned from. Access keys are sealed and never returned. Object lock is available on `s3` only, and the chunk size is fixed for life. Warnings in the answer name what the service cannot check for itself.\n\nOperator only: needs a system-scoped token.",
        "operationId": "registerRepository",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "access_key": "<access-key>",
                "bucket": "backups-bucharest",
                "driver": "s3",
                "endpoint": "https://s3.example.net",
                "kind": "chunk",
                "name": "objects-bucharest",
                "object_lock": {
                  "min_retention": "720h",
                  "mode": "compliance"
                },
                "region": "eu-ro-1",
                "secret_key": "<secret-key>",
                "site_name": "bucharest",
                "soft_delete_grace": "336h"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateRepositoryReq"
              }
            }
          },
          "description": "The repository to register. A field that does not apply to the kind or driver is refused, and the refusal names it.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "bucket": "backups-bucharest",
                  "chunk_size": 4194304,
                  "created_at": "2026-10-02T09:30:00Z",
                  "credentials_sealed": true,
                  "driver": "s3",
                  "endpoint": "https://s3.example.net",
                  "id": "3f2b8c1e-6d4a-4b7f-9e21-5a8c7d9e0f13",
                  "kind": "chunk",
                  "name": "objects-bucharest",
                  "object_lock": {
                    "changeable_until": "2026-10-05T09:30:00Z",
                    "min_retention": 2592000000000000,
                    "mode": "compliance"
                  },
                  "region": "eu-ro-1",
                  "site_id": "a1d4e7b2-3c5f-4a8e-b9d0-2e4f6a8c1b3d",
                  "soft_delete_grace": 1209600000000000,
                  "updated_at": "2026-10-02T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/RepositoryView"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `repository:register`."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: A repository with this name is already registered."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A field the `kind` or `driver` needs is missing: `name`, `site_name`, `kind`, `driver`, `bucket`, `pool` or `prefix`. A field is sent that the `kind` or `driver` does not use, such as `bucket` or `chunk_size` on an rbd tier, or `pool` on the `s3` driver. `object_lock` is asked of a driver other than `s3`, or of an rbd tier. A `driver` is named for an rbd tier. `chunk_size` is not a power of two between 64 KiB and 64 MiB. A duration does not parse. The retention contract is incomplete or inconsistent. Access keys are sent for a driver other than `s3`, or without their pair. A site or replica id is not a UUID. `environment_credentials` is sent with access keys, or for an rbd tier.\n\n`repository_unreadable`: The chunk repository is at another site, and listing it from here with the access keys given, or with none, failed. `detail` names the storage and the error, and nothing was saved. Send access keys that can list it, or `environment_credentials`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`kms_unavailable`: Access keys were sent, but no key manager is configured to seal them."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Register a repository",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      }
    },
    "/v1/repositories/health": {
      "get": {
        "description": "What the last check of each repository at this site found, and the overall verdict.\n\nOperator only: needs a system-scoped token.",
        "operationId": "getRepositoryHealth",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "checked_at": "2026-10-02T09:55:12Z",
                  "repositories": [
                    {
                      "checked_at": "2026-10-02T09:55:12Z",
                      "checks": [
                        {
                          "latency_ns": 38000000,
                          "name": "list",
                          "ok": true
                        },
                        {
                          "latency_ns": 41000000,
                          "name": "put",
                          "ok": true
                        },
                        {
                          "latency_ns": 0,
                          "name": "no_default_retention",
                          "ok": true
                        },
                        {
                          "latency_ns": 22000000,
                          "name": "version_listing",
                          "ok": true
                        },
                        {
                          "latency_ns": 30000000,
                          "name": "conditional_put",
                          "ok": true
                        },
                        {
                          "latency_ns": 27000000,
                          "name": "checksum_enforced",
                          "ok": true
                        },
                        {
                          "latency_ns": 12000000,
                          "name": "head",
                          "ok": true
                        },
                        {
                          "latency_ns": 19000000,
                          "name": "get",
                          "ok": true
                        },
                        {
                          "latency_ns": 15000000,
                          "name": "delete",
                          "ok": true
                        },
                        {
                          "latency_ns": 10000000,
                          "name": "object_lock",
                          "ok": true
                        }
                      ],
                      "driver": "s3",
                      "immutability_verified": true,
                      "immutable": true,
                      "latency_ns": 214000000,
                      "name": "objects-bucharest",
                      "readable": true,
                      "repository_id": "3f2b8c1e-6d4a-4b7f-9e21-5a8c7d9e0f13",
                      "site_id": "a1d4e7b2-3c5f-4a8e-b9d0-2e4f6a8c1b3d",
                      "status": "ok",
                      "writable": true,
                      "write_probed": true
                    }
                  ],
                  "status": "ok"
                },
                "schema": {
                  "$ref": "#/components/schemas/RepoHealthReport"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `repository:usage`."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show repository health",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      }
    },
    "/v1/repositories/{id}": {
      "delete": {
        "description": "Removes the registration and leaves the stored data where it is. Refused while a plan is placed on the repository, or while it still holds data under object lock.\n\nOperator only: needs a system-scoped token.",
        "operationId": "deleteRepository",
        "parameters": [
          {
            "description": "The repository's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `repository:register`."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no repository has it."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: A plan's policy places backups on this repository as its backup, instant or copy tier. `detail` names the plans.\n\n`repository_immutable`: The repository still holds backups whose retention has not elapsed. Or it holds unreferenced objects or old object versions that the storage's lock does not release yet."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Deregister a repository",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      },
      "get": {
        "description": "Operator only: needs a system-scoped token.",
        "operationId": "getRepository",
        "parameters": [
          {
            "description": "The repository's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "bucket": "backups-bucharest",
                  "checksum_trailers": true,
                  "chunk_size": 4194304,
                  "created_at": "2026-10-02T09:30:00Z",
                  "credentials_sealed": true,
                  "driver": "s3",
                  "endpoint": "https://s3.example.net",
                  "id": "3f2b8c1e-6d4a-4b7f-9e21-5a8c7d9e0f13",
                  "kind": "chunk",
                  "name": "objects-bucharest",
                  "object_lock": {
                    "changeable_until": "2026-10-05T09:30:00Z",
                    "min_retention": 2592000000000000,
                    "mode": "compliance"
                  },
                  "reachable_from_sites": [
                    "c7e9a2b4-1d3f-4e5a-8b6c-9d0e1f2a3b4c"
                  ],
                  "region": "eu-ro-1",
                  "site_id": "a1d4e7b2-3c5f-4a8e-b9d0-2e4f6a8c1b3d",
                  "soft_delete_grace": 1209600000000000,
                  "updated_at": "2026-10-02T11:05:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/RepositoryView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `repository:usage`."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no repository has it."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a repository",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      },
      "patch": {
        "description": "Changes the endpoint, region, credentials, reachable sites or object lock. Credentials are replaced as a pair. What decides where data lives, such as the bucket, pool or chunk size, cannot change. Once an object lock's grace period has passed, its mode and granularity are fixed, and its minimum and maximum retention can only rise. A change that would leave a policy or vault unable to keep its terms is refused.\n\nOperator only: needs a system-scoped token.",
        "operationId": "updateRepository",
        "parameters": [
          {
            "description": "The repository's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "access_key": "<new-access-key>",
                "endpoint": "https://s3-2.example.net",
                "secret_key": "<new-secret-key>"
              },
              "schema": {
                "$ref": "#/components/schemas/PatchRepositoryReq"
              }
            }
          },
          "description": "The fields to change. Credentials are replaced as a pair. Changes to the retention contract or the soft-delete grace also need the `repository:lock` permission.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "bucket": "backups-bucharest",
                  "chunk_size": 4194304,
                  "created_at": "2026-10-02T09:30:00Z",
                  "credentials_sealed": true,
                  "driver": "s3",
                  "endpoint": "https://s3-2.example.net",
                  "id": "3f2b8c1e-6d4a-4b7f-9e21-5a8c7d9e0f13",
                  "kind": "chunk",
                  "name": "objects-bucharest",
                  "object_lock": {
                    "changeable_until": "2026-10-05T09:30:00Z",
                    "min_retention": 2592000000000000,
                    "mode": "compliance"
                  },
                  "region": "eu-ro-1",
                  "site_id": "a1d4e7b2-3c5f-4a8e-b9d0-2e4f6a8c1b3d",
                  "soft_delete_grace": 1209600000000000,
                  "updated_at": "2026-10-02T12:40:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/RepositoryView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `repository:register`. Or the request changes `object_lock`, `clear_object_lock` or `soft_delete_grace` and they do not grant `repository:lock`."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no repository has it."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The retention contract is past its grace period, and the request would remove it, change its `mode` or `granularity`, lower `min_retention` or `max_retention`, or restate `changeable_until`. `detail` names the date the grace period ended.\n\n`conflict`: Raising `min_retention` would leave active policies keeping backups here for less. Or lowering `max_retention` would leave policies keeping them for longer. `fields` names the policies.\n\n`conflict`: The vault on this repository offers terms the change would break. `fields` names the vault."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: Access keys are sent for a driver other than `s3`, or without their pair. `endpoint` or `region` is sent for a driver other than `s3`. `soft_delete_grace` is sent for an rbd tier. A site or replica id is not a UUID. A duration does not parse. The retention contract is incomplete or inconsistent. Object lock is asked of a driver other than `s3`. `environment_credentials` is sent with access keys, or for an rbd tier.\n\n`repository_unreadable`: The chunk repository is at another site, the request changes its access keys, endpoint or region, and listing it from here with the result failed. Nothing was changed."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`kms_unavailable`: New access keys were sent, but no key manager is configured to seal them."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Change a repository",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      }
    },
    "/v1/repositories/{id}/domain-keys": {
      "get": {
        "description": "Each dedup domain in the repository with its key's fingerprint, never the key itself. Comparing fingerprints confirms another cloud holds the same key before a restore there needs it. A key this cloud cannot unwrap is reported as unreadable, with the reason.\n\nOperator only: needs a system-scoped token.",
        "operationId": "listDomainKeys",
        "parameters": [
          {
            "description": "The repository's id, or its name.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "domain_keys": [
                    {
                      "domain": "default",
                      "fingerprint": "3f9a1c0e7b2d4a65",
                      "kek_ref": "barbican"
                    },
                    {
                      "domain": "payments",
                      "fingerprint": "",
                      "kek_ref": "barbican",
                      "unreadable": "the key-encryption key was removed from the key manager"
                    }
                  ],
                  "repository": "objects-bucharest",
                  "repository_id": "3f2b8c1e-6d4a-4b7f-9e21-5a8c7d9e0f13"
                },
                "schema": {
                  "$ref": "#/components/schemas/DomainKeyList"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles grant neither `repository:register` nor `peer:repository:read`, which another cloud's service reads key fingerprints with."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No repository has this id or name."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List a repository's domain keys",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      }
    },
    "/v1/repositories/{id}/domain-keys/export": {
      "post": {
        "description": "Wraps one dedup domain's key under a passphrase, so another cloud can import it and read the backups. Every export is audited. The passphrase is never stored or returned. Send it separately from the envelope.\n\nOperator only: needs a system-scoped token.",
        "operationId": "exportDomainKey",
        "parameters": [
          {
            "description": "The repository's id, or its name.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "domain": "default",
                "passphrase": "<passphrase>"
              },
              "schema": {
                "$ref": "#/components/schemas/ExportKeyReq"
              }
            }
          },
          "description": "The dedup domain whose key to export, and the passphrase to wrap it under.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "envelope": {
                    "ciphertext": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
                    "domain": "default",
                    "exported_at": "2026-10-02T10:04:31Z",
                    "exported_by": "admin",
                    "iterations": 600000,
                    "kdf": "pbkdf2-sha256",
                    "magic": "FRYK",
                    "nonce": "AAAAAAAAAAAAAAAA",
                    "repository": "objects-bucharest",
                    "salt": "AAAAAAAAAAAAAAAAAAAAAA==",
                    "source_cloud": "eu-ro-1",
                    "version": 1
                  },
                  "fingerprint": "3f9a1c0e7b2d4a65"
                },
                "schema": {
                  "$ref": "#/components/schemas/ExportKeyView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `repository:register`."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No repository has this id or name."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`domain_key_missing`: This cloud holds no key for the dedup domain in this repository. `detail` names the cloud that wrote it, when the repository records that."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `domain` or `passphrase` is missing.\n\n`domain_not_encrypted`: `domain` is `plain`, which holds unencrypted backups and has no key."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`kms_unavailable`: No key manager is configured, so the domain key cannot be unwrapped."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Export a domain key",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      }
    },
    "/v1/repositories/{id}/domain-keys/import": {
      "post": {
        "description": "Installs a key exported from another cloud, wrapped under this cloud's own key manager. The repository is this cloud's registration of the storage the key belongs to, whatever either cloud named it. Before anything is kept, the key is checked against that storage: it must open what the dedup domain already holds there, or, in a domain holding nothing yet, the domain's key marker must name the repository and cloud the envelope was exported from. A domain keeps one key for life: importing the same key again changes nothing, and a different one is refused.\n\nOperator only: needs a system-scoped token.",
        "operationId": "importDomainKey",
        "parameters": [
          {
            "description": "The repository's id, or its name.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "domain": "default",
                "envelope": {
                  "ciphertext": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
                  "domain": "default",
                  "exported_at": "2026-10-02T10:04:31Z",
                  "exported_by": "admin",
                  "iterations": 600000,
                  "kdf": "pbkdf2-sha256",
                  "magic": "FRYK",
                  "nonce": "AAAAAAAAAAAAAAAA",
                  "repository": "objects-bucharest",
                  "salt": "AAAAAAAAAAAAAAAAAAAAAA==",
                  "source_cloud": "eu-ro-1",
                  "version": 1
                },
                "passphrase": "<passphrase>"
              },
              "schema": {
                "$ref": "#/components/schemas/ImportKeyReq"
              }
            }
          },
          "description": "The envelope another cloud's export returned, its passphrase, and the dedup domain.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "domain": "default",
                  "fingerprint": "3f9a1c0e7b2d4a65",
                  "kek_ref": "barbican"
                },
                "schema": {
                  "$ref": "#/components/schemas/DomainKeyView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `repository:register`.\n\n`forbidden`: The envelope will not open. A wrong passphrase and a damaged envelope are deliberately not told apart."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No repository has this id or name."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: The envelope was exported for another dedup domain.\n\n`conflict`: The key opens nothing the dedup domain holds in this repository. Or the domain holds nothing yet and its key marker names another repository or cloud, or there is no marker. The envelope is for other storage than this repository names; nothing is kept.\n\n`conflict`: The dedup domain already holds a different key, or one this cloud cannot unwrap to compare. Nothing is changed, and importing the same key again succeeds."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `domain`, `passphrase` or `envelope` is missing. Or the envelope is malformed: an unknown format or version, an unsupported derivation, fewer than 100000 iterations, or a salt or nonce of the wrong length.\n\n`domain_not_encrypted`: `domain` is `plain`, which holds unencrypted backups and has no key."
          },
          "502": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Gateway.\n\n`service_unavailable`: The repository could not be opened or listed to check the key against, so nothing was kept."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`kms_unavailable`: No key manager is configured, or the configured one cannot hold a key supplied from outside."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Import a domain key",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      }
    },
    "/v1/repositories/{id}/inventory": {
      "get": {
        "description": "Every backup found in the repository, including those another cloud wrote. Each one carries the recovery binding that covers it. Where a project of the same id exists here, that project is proposed as the owner, to confirm. The answer also lists the parts of the repository that cannot be read yet, such as a dedup domain whose key has not been imported. With `view=groups` it lists instead the protection groups the repository describes, each at the newest version of its document read here, with the recovery registered here for it, if any; a domain whose key is missing hides its groups and is listed as unreadable.\n\nOperator only: needs a system-scoped token.",
        "operationId": "getRepositoryInventory",
        "parameters": [
          {
            "description": "The repository's id or name.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`points` (the default) lists backups. `groups` lists protection group documents.",
            "in": "query",
            "name": "view",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups written by the cloud with this name.",
            "in": "query",
            "name": "origin_cloud",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups of this project, in the cloud that wrote them.",
            "in": "query",
            "name": "source_project",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only backups of this resource.",
            "in": "query",
            "name": "resource_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "entries": [
                    {
                      "consistency": "filesystem",
                      "coverage": "full",
                      "dedup_domain": "payments",
                      "encryption": "aes256-gcm",
                      "first_seen_at": "2026-10-02T06:05:00Z",
                      "observed_mtime": "2026-10-02T06:00:43Z",
                      "origin_cloud": "eu-ro-1",
                      "origin_region": "eu-ro-1",
                      "origin_uid": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a",
                      "point_created_at": "2026-10-02T06:00:41Z",
                      "point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                      "point_key": "payments/points/1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b.json",
                      "repository_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
                      "resource_id": "d3b07384-d113-4ec4-9a0e-6c1f2b3a4d5e",
                      "resource_kind": "instance",
                      "schema_version": 1,
                      "source_project": "7e6d5c4b3a291807f6e5d4c3b2a19080",
                      "target_proposal": {
                        "enabled": true,
                        "name": "payments",
                        "project_id": "7e6d5c4b3a291807f6e5d4c3b2a19080",
                        "reason": "a project with this id exists in this cloud, which is what a shared Keystone across regions produces. Confirm it is the same project before binding"
                      }
                    }
                  ],
                  "limit": 50,
                  "repository": "ovh-bucharest",
                  "unreadable": [
                    {
                      "cursor_at": "0001-01-01T00:00:00Z",
                      "dedup_domain": "billing",
                      "kek_ref": "barbican:8a7b6c5d-4e3f-4a2b-9c1d-0e9f8a7b6c5d",
                      "kms": "barbican",
                      "last_error": "no key for this dedup domain is held here",
                      "last_pass_at": "2026-10-02T06:05:00Z",
                      "needs_key": true,
                      "origin_cloud": "eu-ro-1",
                      "points_seen": 0,
                      "repository_id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/InventoryResponse"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No repository with this id or name is registered."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `view` is neither `points` nor `groups`."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`service_unavailable`: This service does not catalogue repositories.\n\n`dr_not_enabled`: `view=groups` was asked for and disaster recovery is not enabled here."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List what a repository holds",
        "tags": [
          "Recovery"
        ],
        "x-access": "operator"
      }
    },
    "/v1/repositories/{id}/usage": {
      "get": {
        "description": "Physical size, what each project references, the saving that deduplication across projects brings, data held under lock and when it is released, and the state of cleanup. A figure with no measurement yet is listed under `unavailable` with the reason.\n\nOperator only: needs a system-scoped token.",
        "operationId": "getRepositoryUsage",
        "parameters": [
          {
            "description": "The repository's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "ceph_trash_pinned_bytes": 0,
                  "cross_tenant_margin": 579205521408,
                  "gc_last_completed_at": "2026-10-02T03:12:40Z",
                  "gc_last_duration_seconds": 1840,
                  "held_bytes": 52428800,
                  "held_objects": 14,
                  "held_release": [
                    {
                      "bytes": 52428800,
                      "day": "2026-10-09"
                    }
                  ],
                  "kind": "chunk",
                  "leaked_disk_bytes": 42949672960,
                  "locked_bytes": 1503238553600,
                  "name": "objects-bucharest",
                  "object_count": 512340,
                  "physical_bytes": 1832519278592,
                  "reclaimable_pending_bytes": 2684354560,
                  "release_schedule": [
                    {
                      "bytes": 751619276800,
                      "month": "2026-11-01T00:00:00Z"
                    },
                    {
                      "bytes": 751619276800,
                      "month": "2026-12-01T00:00:00Z"
                    }
                  ],
                  "repository_id": "3f2b8c1e-6d4a-4b7f-9e21-5a8c7d9e0f13",
                  "sum_tenant_referenced": 2411724800000,
                  "tenants": [
                    {
                      "computed_at": "2026-10-02T03:12:40Z",
                      "exclusive_bytes": 96636764160,
                      "logical_allocated_bytes": 9663676416000,
                      "logical_provisioned_bytes": 21474836480000,
                      "point_count": 412,
                      "project_id": "8c2e6b1f4a3d4e5f9a0b1c2d3e4f5a6b",
                      "referenced_bytes": 1610612736000,
                      "resource_count": 23,
                      "soft_deleted_bytes": 2147483648
                    },
                    {
                      "computed_at": "2026-10-02T03:12:40Z",
                      "exclusive_bytes": 42949672960,
                      "logical_allocated_bytes": 4294967296000,
                      "logical_provisioned_bytes": 8589934592000,
                      "point_count": 186,
                      "project_id": "1f3e5d7c9b2a4f6e8d0c2b4a6e8f0d2c",
                      "referenced_bytes": 801112064000,
                      "resource_count": 9
                    },
                    {
                      "logical_allocated_bytes": 107374182400,
                      "logical_provisioned_bytes": 536870912000,
                      "point_count": 3,
                      "project_id": "5a7c9e1b3d2f4a6c8e0b2d4f6a8c0e1f",
                      "resource_count": 1,
                      "unavailable": {
                        "exclusive_bytes": "sweep_pending",
                        "referenced_bytes": "sweep_pending"
                      }
                    }
                  ],
                  "verified_coverage_pct_30d": 96.9,
                  "versions": {
                    "beyond_ceiling": 0,
                    "delete_markers": 0,
                    "foreign_objects": 0,
                    "noncurrent_bytes": 1333788672,
                    "noncurrent_objects": 318,
                    "taken_at": "2026-10-01T03:20:00Z"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/RepoUsageResponse"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `repository:usage`."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no repository has it."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a repository's usage",
        "tags": [
          "Repositories"
        ],
        "x-access": "operator"
      }
    },
    "/v1/resources": {
      "get": {
        "description": "Every server, volume and bucket in the project, with whether it can be protected whole, in part or not at all, and why. `view=names` answers only the kind, id, name and size of each, which is much faster.\n\nAny reader of the project the token is scoped to.",
        "operationId": "listResources",
        "parameters": [
          {
            "description": "Operator only: act on this project instead of the token's own.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only resources of this kind, `volume`, `instance` or `bucket`.",
            "in": "query",
            "name": "kind",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`true` for only the resources that can be protected whole. Cannot be combined with `view=names`.",
            "in": "query",
            "name": "protectable",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "`names` to answer only each resource's `kind`, `id`, `name` and `size_bytes`, without working out whether it can be protected, which is much faster. Leave it out for the full answer.",
            "in": "query",
            "name": "view",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResourceViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not allow listing plans in its project, or `project_id` names another project the token may not read."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`discovery_unavailable`: This cloud is set up to inspect neither block storage nor object storage."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List what can be protected",
        "tags": [
          "Resources"
        ],
        "x-access": "reader"
      }
    },
    "/v1/restores": {
      "get": {
        "description": "Attachments appear here too, with `mode` `attach`.\n\nAny member of the project the token is scoped to.",
        "operationId": "listRestores",
        "parameters": [
          {
            "description": "Operator only: act on this project instead of the token's own.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only restores of this backup.",
            "in": "query",
            "name": "point_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only restores in this state. Repeat for several. `pending`, `claimed` and `running` are in progress. `succeeded` finished. `failed`, `dead_letter` and `cancelled` did not. An attachment also uses `detaching` and `detached`.",
            "in": "query",
            "name": "state",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RestoreViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not restore in this project. Or `project_id` names a project it may not read."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List restores",
        "tags": [
          "Restores"
        ],
        "x-access": "member"
      },
      "post": {
        "description": "`mode` is `volume`, `instance` or `bucket` to restore as something new, or `in_place` to write the backup back over the volume or server it came from. A new volume or server whose backup is on an instant tier at this site is cloned from it, as job kind `instant_restore`: each volume exists within seconds in the target project, nothing is read from the repository, and the clone is copied in full in the background. Every other restore reads the repository copy. Restored as new, a server can take a different `flavor`, `networks`, `key_name` and `security_groups`, and `resource_ids` restores only the named disks. `mapping_profile` applies a saved recovery profile. Anything it cannot resolve here must be confirmed by name in `confirm_unresolved`. A backup that does not hold every disk of its resource is refused until `acknowledge_partial` is set. Only an operator can restore into another project, with `target_project_id`. Every restore acts in its target project through a delegation the caller's token grants, and the identity service delegates only roles the caller holds in that project itself: an admin role on the system or on another project does not count, so an operator account needs a role, such as `member`, in every project it restores into.\n\nAny member of the project the token is scoped to.",
        "operationId": "createRestore",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "flavor": "m1.large",
                "key_name": "ops",
                "mode": "instance",
                "name": "web-01-restored",
                "networks": [
                  "private"
                ],
                "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                "security_groups": [
                  "default",
                  "web"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateRestoreReq"
              }
            }
          },
          "description": "The restore to start. `point_id` and `mode` are required. `flavor`, `networks`, `key_name` and `security_groups` apply to `mode` `instance` only, and `resource_ids` to `mode` `volume` or `instance`. The answer is the restore, already queued. Poll GET /v1/restores/{id} until it succeeds or fails.",
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-02T09:31:02Z",
                  "elevated": false,
                  "estimated_restore_seconds": 221,
                  "id": "2e5b8d1f-4a7c-4b0e-9d3a-6c9f2b5e8a1d",
                  "job_id": "1c4f7a0d-3b6e-4c9a-8f2d-5a8c1e4b7d0a",
                  "mode": "instance",
                  "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "project_id": "4f1c2a7d9b3e4e0a8c5d6b7a8f9e0d1c",
                  "requested_by": "7a1e3c5b9d2f4a6c8e0b1d3f5a7c9e2b",
                  "source_tier": "chunk",
                  "state": "pending",
                  "target_project_id": "4f1c2a7d9b3e4e0a8c5d6b7a8f9e0d1c"
                },
                "schema": {
                  "$ref": "#/components/schemas/RestoreView"
                }
              }
            },
            "description": "Accepted"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type.\n\n`restore_trust_refused`: The token cannot grant the delegation the restore needs, because none was carried or it was issued from an application credential."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not restore in this project, or may not restore in place. Or it names another project in `target_project_id` without operator rights.\n\n`restore_trust_refused`: The caller holds no role on the target project to delegate. Grant the account a role in that project and restore again."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The backup does not exist or is not visible to this token. Or `mapping_profile` names no saved profile for the backup's origin."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`point_not_restorable`: The backup's state is not `committed` or `verified`, for example because it is still being taken, is broken or is being erased.\n\n`partial_restore_unacknowledged`: The backup does not contain every disk of its resource and `acknowledge_partial` is not `true`. `refusals` and `coverage` list what is missing.\n\n`tier_unavailable`: `tier` names a copy that cannot serve this request, and the detail says why: for the instant tier, an in-place restore, a volume type, zone or mapping profile named, a disk that is not a Cinder volume, or a tier this service cannot clone from. Or no copy can be read now, for example because the backup is held only on the instant tier and not yet written to the repository, and the request is one the instant tier cannot serve.\n\n`mapping_unresolved`: `mapping_profile` leaves elements unresolved that `confirm_unresolved` does not name. Or more than one cloud wrote backups under the backup's origin name.\n\n`domain_key_missing`: `mapping_profile` needs the backup's record, and this cloud holds no key that opens it."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A field is not acceptable. `mode` is missing or unknown. `point_id` is not a UUID. `mode` does not suit the backup: `bucket` for a backup of disks, or anything else for a bucket. `tier` is not `chunk` or `rbd`. `flavor`, `networks`, `key_name`, `security_groups` or `resource_ids` were sent with a `mode` that does not build that. `mode` is `bucket` and this cloud has no object gateway."
          },
          "502": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Gateway.\n\n`restore_trust_refused`: The identity service refused the delegation.\n\n`service_unavailable`: `mapping_profile` was named and this cloud's inventory could not be read."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`kms_unavailable`: The backup's record is encrypted and this site has no key manager configured."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Restore a backup",
        "tags": [
          "Restores"
        ],
        "x-access": "member"
      }
    },
    "/v1/restores/estimate": {
      "get": {
        "description": "For each copy of the backup: whether a restore can read it, how many seconds it would take and how that figure was reached. `fastest_tier` is the copy with the shortest estimate. The instant tier is listed as available only where a restore would really clone from it, and it serves new volumes and servers only.\n\nAny member of the project the token is scoped to.",
        "operationId": "estimateRestore",
        "parameters": [
          {
            "description": "The backup to estimate. Required.",
            "in": "query",
            "name": "point_id",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "fastest_tier": "chunk",
                  "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "tiers": [
                    {
                      "available": false,
                      "basis": "rbd_clone_o1",
                      "estimated_restore_seconds": 0,
                      "reason": "no rbd tier holds this point",
                      "tier": "rbd"
                    },
                    {
                      "available": true,
                      "basis": "chunk_count_x_assumed_throughput",
                      "bytes": 46322794496,
                      "estimated_restore_seconds": 221,
                      "repository_id": "e4f7a0c3-6b9d-4f2e-8a5c-1d4f7b0e3a6c",
                      "tier": "chunk"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/EstimateResponse"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not restore in the backup's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The backup does not exist or is not visible to this token."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Estimate how long a restore would take",
        "tags": [
          "Restores"
        ],
        "x-access": "member"
      }
    },
    "/v1/restores/{id}": {
      "get": {
        "description": "Its state, what it has created so far and how long each step took.\n\nAny member of the project the token is scoped to.",
        "operationId": "getRestore",
        "parameters": [
          {
            "description": "The id of the restore.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "actual_restore_seconds": 412,
                  "created_at": "2026-10-02T09:31:02Z",
                  "created_resources": {
                    "instance:c2a7f0e4-1b3d-4e5f-8a9b-0c1d2e3f4a5b": "f3a6c9e2-5b8d-4f1a-9c4e-7d0a3f6b9c2e",
                    "port:b7e0a3d6-9c2f-4b5e-8a1d-4f7c0e3a6d9b": "a9c2e5b8-1d4f-4a7c-9e0b-3d6f9a2c5e8b",
                    "volume:7e9a1b2c-3d4e-4f5a-8b6c-9d0e1f2a3b4c": "0d3a6f9c-2e5b-4a8d-8b1f-4c7e0a3d6f9b"
                  },
                  "elevated": false,
                  "estimated_restore_seconds": 221,
                  "finished_at": "2026-10-02T09:37:54Z",
                  "id": "2e5b8d1f-4a7c-4b0e-9d3a-6c9f2b5e8a1d",
                  "job_id": "1c4f7a0d-3b6e-4c9a-8f2d-5a8c1e4b7d0a",
                  "mode": "instance",
                  "point_id": "5f0c1c9e-8a3e-4c1b-9a51-2f6f7a0d3b11",
                  "project_id": "4f1c2a7d9b3e4e0a8c5d6b7a8f9e0d1c",
                  "requested_by": "7a1e3c5b9d2f4a6c8e0b1d3f5a7c9e2b",
                  "source_tier": "chunk",
                  "stages": [
                    {
                      "detail": "1 volume created",
                      "ended": "2026-10-02T09:31:41Z",
                      "name": "provisioning",
                      "seconds": 21.4,
                      "started": "2026-10-02T09:31:20Z"
                    },
                    {
                      "ended": "2026-10-02T09:36:58Z",
                      "name": "writing_disks",
                      "seconds": 317.2,
                      "started": "2026-10-02T09:31:41Z"
                    },
                    {
                      "ended": "2026-10-02T09:37:31Z",
                      "name": "creating_instance",
                      "seconds": 33.1,
                      "started": "2026-10-02T09:36:58Z"
                    },
                    {
                      "ended": "2026-10-02T09:37:54Z",
                      "name": "starting_instance",
                      "seconds": 22.8,
                      "started": "2026-10-02T09:37:31Z"
                    }
                  ],
                  "state": "succeeded",
                  "target_project_id": "4f1c2a7d9b3e4e0a8c5d6b7a8f9e0d1c"
                },
                "schema": {
                  "$ref": "#/components/schemas/RestoreView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token may not restore in the restore's project."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no restore has it, or it belongs to another project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a restore",
        "tags": [
          "Restores"
        ],
        "x-access": "member"
      }
    },
    "/v1/retention/simulate": {
      "post": {
        "description": "Shows which existing backups a policy, or a proposed `spec` for it, would keep and expire, which are locked, and how much space expiring them would free in the project. `plan_id` narrows it to one plan. `at` evaluates it at another time.\n\nOperator only: needs a system-scoped token.",
        "operationId": "simulateRetention",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "plan_id": "9b4d6f8a-0c2e-4a1b-8d3f-5e7a9c1b3d5f",
                "policy_id": "6e8a0c2d-4f1b-4d3e-a5c7-9b1d3f5e7a90",
                "spec": {
                  "keep": {
                    "daily": 7,
                    "weekly": 4
                  },
                  "schedule": {
                    "every": "24h"
                  },
                  "tiers": {
                    "backup": {
                      "repository_id": "3f2b8c1e-6d4a-4b7f-9e21-5a8c7d9e0f13"
                    }
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/SimulateRetentionReq"
              }
            }
          },
          "description": "The policy to preview, optionally narrowed to one plan, with a proposed spec to evaluate instead of the current one and a time to evaluate at.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "expired": 1,
                  "kept": 2,
                  "locked": 2,
                  "method": "manifest_walk",
                  "plan_id": "9b4d6f8a-0c2e-4a1b-8d3f-5e7a9c1b3d5f",
                  "points": [
                    {
                      "committed_at": "2026-10-01T22:00:00Z",
                      "expires_at": "2026-10-08T22:00:00Z",
                      "kept": true,
                      "kept_by": "daily",
                      "locked_until": "2026-11-02T00:00:00Z",
                      "point_id": "2c4e6a8b-0d1f-4b3c-9e5a-7f9b1d3e5a7c",
                      "was": "2026-10-31T22:00:00Z"
                    },
                    {
                      "committed_at": "2026-09-10T22:00:00Z",
                      "expires_at": "2026-09-17T22:00:00Z",
                      "kept": false,
                      "locked_until": "2026-10-12T00:00:00Z",
                      "point_id": "4e6a8c0d-2f1b-4d5e-8a7c-9b1d3f5e7a92",
                      "was": "2026-10-10T22:00:00Z"
                    },
                    {
                      "committed_at": "2026-08-20T22:00:00Z",
                      "expires_at": "2026-08-27T22:00:00Z",
                      "kept": false,
                      "locked_until": "2026-09-21T00:00:00Z",
                      "point_id": "7a9c1e3f-5b2d-4f6a-8c0e-2d4f6a8b0c1e",
                      "was": "2026-10-20T22:00:00Z"
                    }
                  ],
                  "policy_id": "6e8a0c2d-4f1b-4d3e-a5c7-9b1d3f5e7a90",
                  "reclaimable_bytes": 4831838208,
                  "shortens": true
                },
                "schema": {
                  "$ref": "#/components/schemas/SimulateRetentionResp"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `policy:write`."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No policy has `policy_id`. Or `plan_id` names no plan of that policy."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `policy_id` is missing or not a UUID. `plan_id` is not a UUID. A field of `spec` is not valid. The `keep` ladder of `spec` retains nothing."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Preview a retention change",
        "tags": [
          "Retention"
        ],
        "x-access": "operator"
      }
    },
    "/v1/sites": {
      "get": {
        "description": "The sites a protection policy's recovery section can name. `local` marks this one, which is where every protection group here runs.\n\nAny member of the project the token is scoped to.",
        "operationId": "listSites",
        "parameters": [
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token has no member role in its project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List sites",
        "tags": [
          "Disaster recovery"
        ],
        "x-access": "member"
      }
    },
    "/v1/standbys": {
      "get": {
        "description": "The warm copies kept for the project's resources, with the backup each one holds and whether it is current.\n\nAny reader of the project the token is scoped to.",
        "operationId": "listStandbys",
        "parameters": [
          {
            "description": "Operator only: list this project's standbys instead of the token's own. A system-scoped token without it lists every project's standbys.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StandbyPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token has no reader role in its project. Or `project_id` names another project and the token may not read other projects."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List standbys",
        "tags": [
          "Standbys"
        ],
        "x-access": "reader"
      },
      "post": {
        "description": "Keeps a warm copy of a protected resource's disks in a pool at a site, brought up to date after every backup. Recovering it then takes as long as a boot, not as long as copying its data. The pool must hold standbys only, because every update overwrites what is in it. Without `site_id` the copy is kept at this site.\n\nOperator only: needs a system-scoped token.",
        "operationId": "createStandby",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                "resource_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                "site_id": "3b5d5c37-1d2e-4f0a-9b6e-7c8d9e0f1a2b",
                "target_pool": "standby",
                "target_prefix": "sb-"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateStandbyReq"
              }
            }
          },
          "description": "The project it belongs to, the resource to copy and the pool to keep the copy in.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-10-02T08:30:00Z",
                  "domain_id": "default",
                  "id": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d",
                  "project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                  "resource_id": "8f14e45f-ceea-467a-9a3f-1b2c3d4e5f60",
                  "site_id": "3b5d5c37-1d2e-4f0a-9b6e-7c8d9e0f1a2b",
                  "state": "seeding",
                  "target_pool": "standby",
                  "target_prefix": "sb-",
                  "updated_at": "2026-10-02T08:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Standby"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token is not a system-scoped admin token."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: `project_id` names a project that does not exist here or is disabled. Or a standby for this resource already exists at this site."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: `project_id`, `resource_id` or `target_pool` is missing. `site_id` is not a registered site. `group_id` is not a UUID, or names no group in `project_id`. `domain_id` does not match the project's domain, or is missing when the project cannot be looked up."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Register a standby",
        "tags": [
          "Standbys"
        ],
        "x-access": "operator"
      }
    },
    "/v1/standbys/{id}": {
      "delete": {
        "description": "Stops updating the copy and forgets it. Its disks are left in the pool.\n\nOperator only: needs a system-scoped token.",
        "operationId": "deleteStandby",
        "parameters": [
          {
            "description": "The standby's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The id is not a UUID."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No standby with this id exists, or the token may not register standbys."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Remove a standby",
        "tags": [
          "Standbys"
        ],
        "x-access": "operator"
      },
      "get": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "getStandby",
        "parameters": [
          {
            "description": "The standby's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "applied_at": "2026-10-02T06:04:51Z",
                  "applied_point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                  "created_at": "2026-09-20T11:00:00Z",
                  "domain_id": "default",
                  "id": "6c5b4a39-2817-4f06-a5e4-d3c2b1a09f8e",
                  "project_id": "4f3c2b1a0e9d8c7b6a5f4e3d2c1b0a99",
                  "resource_id": "d3b07384-d113-4ec4-9a0e-6c1f2b3a4d5e",
                  "site_id": "3b5d5c37-1d2e-4f0a-9b6e-7c8d9e0f1a2b",
                  "state": "current",
                  "target_pool": "standby",
                  "target_prefix": "acc-dr-",
                  "updated_at": "2026-10-02T06:04:51Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/Standby"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The id is not a UUID."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No standby with this id exists, or the token may not read its project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a standby",
        "tags": [
          "Standbys"
        ],
        "x-access": "reader"
      }
    },
    "/v1/standbys/{id}/promote": {
      "post": {
        "description": "Creates volumes in the standby's project from its `applied_snap`, boot disk first, ready to attach or boot from. They hold `applied_point_id` exactly, whatever state the standby is in: a standby that is updating or whose last update failed still promotes to the backup it last finished. No server is created. Each volume is cloned, registered with the block storage service and handed to the project through a delegation the caller's token grants; every volume is cloned in full in the background. Either every disk becomes a volume or none does. The identity service delegates only roles the caller holds in the standby's project itself: an admin role on the system or on another project does not count, so an operator account that promotes standbys needs a role, such as `member`, in every project whose standbys it promotes.\n\nAny member of the project the token is scoped to.",
        "operationId": "promoteStandby",
        "parameters": [
          {
            "description": "The standby's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "name_prefix": "payments-db-recovered-",
                "volume_type": "ceph-ssd"
              },
              "schema": {
                "$ref": "#/components/schemas/PromoteStandbyReq"
              }
            }
          },
          "description": "Optional. How to name the volumes and which volume type to register them with.",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "point_id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
                  "standby_id": "6c5b4a39-2817-4f06-a5e4-d3c2b1a09f8e",
                  "volumes": [
                    {
                      "bootable": true,
                      "name": "payments-db-recovered-d3b07384-d113-4ec4-9a0e-6c1f2b3a4d5e",
                      "size_gb": 80,
                      "source_disk": "d3b07384-d113-4ec4-9a0e-6c1f2b3a4d5e",
                      "volume_id": "0b1c2d3e-4f50-4617-8293-a4b5c6d7e8f9"
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/PromoteStandbyResponse"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type.\n\n`restore_trust_refused`: The token cannot grant the delegation, because none was carried or it was issued from an application credential."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`restore_trust_refused`: The caller holds no role on the standby's project to delegate. Grant the account a role in that project and promote again."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: No standby with this id exists, or the token may not restore into its project."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`standby_has_no_applied_snapshot`: The standby has no `applied_snap` yet, so nothing it holds is known to be a whole backup.\n\n`conflict`: The backup it holds no longer exists. Or the standby moved to a newer backup while it was being promoted; nothing was kept, and promoting again clones the newer one."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`recovery_delegation_missing`: The volumes must end in the standby's project and the service can obtain no delegation to accept them there. Nothing was created."
          },
          "502": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Gateway.\n\n`restore_trust_refused`: The identity service refused the delegation.\n\n`service_unavailable`: Cloning a disk, registering it as a volume or handing it to the project failed. Nothing the promote created is kept."
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Service Unavailable.\n\n`service_unavailable`: The service has no Ceph or block storage access to promote with, or signs in with an application credential, which cannot act on a delegation."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Turn a standby into volumes",
        "tags": [
          "Standbys"
        ],
        "x-access": "member"
      }
    },
    "/v1/usage": {
      "get": {
        "description": "What the project has under protection and what its backups occupy, in total and per repository. `protected_bytes`, `provisioned_bytes`, `point_count` and `protected_resources` are read live, as of `live_as_of`. `stored_bytes`, the project's own deduplicated footprint, is recomputed periodically and is only as fresh as `computed_at`.\n\nAny reader of the project the token is scoped to.",
        "operationId": "getUsage",
        "parameters": [
          {
            "description": "Operator only: act on this project instead of the token's own.",
            "in": "query",
            "name": "project_id",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "computed_at": "2026-10-02T03:12:40Z",
                  "live_as_of": "2026-10-02T03:45:00Z",
                  "point_count": 409,
                  "project_id": "8c2e6b1f4a3d4e5f9a0b1c2d3e4f5a6b",
                  "protected_bytes": 9577777070080,
                  "protected_resources": 23,
                  "provisioned_bytes": 21260088115200,
                  "repositories": [
                    {
                      "computed_at": "2026-10-02T03:12:40Z",
                      "kind": "chunk",
                      "logical_allocated_bytes": 9577777070080,
                      "logical_provisioned_bytes": 21260088115200,
                      "point_count": 409,
                      "repository_id": "3f2b8c1e-6d4a-4b7f-9e21-5a8c7d9e0f13",
                      "stored_bytes": 1608465252352,
                      "vault": {
                        "id": "e2a4c6e8-0b2d-4f6a-8c0e-2b4d6f8a0c3e",
                        "immutable": true,
                        "name": "Locked 30 days"
                      }
                    }
                  ],
                  "stored_bytes": 1608465252352
                },
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token's roles do not grant `usage:read`. Or `project_id` names another project and the token is not an operator's."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show the project's backup usage",
        "tags": [
          "Usage"
        ],
        "x-access": "reader"
      }
    },
    "/v1/vaults": {
      "get": {
        "description": "A project sees the vaults offered to it: the public ones and those its policies keep backups in. An operator sees every vault with its repository and terms as written. Vaults are listed by name.\n\nAny reader of the project the token is scoped to.",
        "operationId": "listVaults",
        "parameters": [
          {
            "description": "How many items to return at most. Defaults to 50; values above 500 are lowered to 500.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The `next_marker` of the previous page. Absent on the first request.",
            "in": "query",
            "name": "marker",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VaultViewPage"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: A query parameter is malformed."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `vault:list` permission."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "List vaults",
        "tags": [
          "Vaults"
        ],
        "x-access": "reader"
      },
      "post": {
        "description": "Gives a repository the name, description and terms that projects see. The terms say how long a manual backup may be kept there, whether it may be kept until deleted, and how many may be taken a day. A vault on a locked repository is immutable. A repository has at most one vault.\n\nOperator only: needs a system-scoped token.",
        "operationId": "createVault",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "description": "Object storage in this region. Backups can be erased once they expire.",
                "manual": {
                  "allowed": true,
                  "max_keep": "2160h",
                  "max_per_day": 2,
                  "until_deleted": true
                },
                "name": "Standard",
                "public": true,
                "repository_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60"
              },
              "schema": {
                "$ref": "#/components/schemas/VaultReq"
              }
            }
          },
          "description": "The new vault. `repository_id` and `name` are required.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-12T09:30:00Z",
                  "description": "Object storage in this region. Backups can be erased once they expire.",
                  "id": "3d9f1b7e-2a4c-4e6b-8d1f-5a7c9e1b3d5f",
                  "immutable": false,
                  "manual": {
                    "keep_at_least": 86400000000000,
                    "keep_at_most": 7776000000000000,
                    "max_per_day": 2,
                    "until_deleted": true
                  },
                  "name": "Standard",
                  "public": true,
                  "repository_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60",
                  "repository_name": "objects-ro1",
                  "sentence": "A backup taken here by hand is kept from a day to 90 days, or until you delete it.",
                  "terms": {
                    "allowed": true,
                    "max_keep": 7776000000000000,
                    "max_per_day": 2,
                    "until_deleted": true
                  },
                  "updated_at": "2026-09-12T09:30:00Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/VaultView"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `vault:write` permission."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: A vault with this name, or on this repository, already exists."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A field is not acceptable, and `fields` names each one. `repository_id` is missing, not a UUID, not registered or not a chunk repository. `name` is empty or longer than 191 characters. `max_keep` does not parse, is below a day or the repository's lock floor, or is above the lock the repository or the cloud allows. `until_deleted` is set on a locked repository. `max_per_day` is negative."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Create a vault",
        "tags": [
          "Vaults"
        ],
        "x-access": "operator"
      }
    },
    "/v1/vaults/{id}": {
      "delete": {
        "description": "Withdraws the vault. Backups already kept in it keep their dates.\n\nOperator only: needs a system-scoped token.",
        "operationId": "deleteVault",
        "parameters": [
          {
            "description": "The vault's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `vault:write` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no vault has it."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Delete a vault",
        "tags": [
          "Vaults"
        ],
        "x-access": "operator"
      },
      "get": {
        "description": "Any reader of the project the token is scoped to.",
        "operationId": "getVault",
        "parameters": [
          {
            "description": "The vault's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-12T09:41:12Z",
                  "description": "Object-locked storage. Nothing kept here can be erased before its date.",
                  "id": "6e8a0c2e-4b6d-4f8a-9c0e-2b4d6f8a0c2e",
                  "immutable": true,
                  "manual": {
                    "keep_at_least": 2592000000000000,
                    "keep_at_most": 31536000000000000,
                    "max_per_day": 1
                  },
                  "name": "Locked",
                  "sentence": "A backup taken here by hand is kept from 30 days to a year, and cannot be erased before its date, by anyone.",
                  "updated_at": "2026-09-20T14:03:55Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/VaultView"
                }
              }
            },
            "description": "OK"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `vault:list` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no vault has it. Or, for a project-scoped token, the vault is neither public nor on the backup repository of a policy offered to the project."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Show a vault",
        "tags": [
          "Vaults"
        ],
        "x-access": "reader"
      },
      "patch": {
        "description": "Fields left out are kept. A vault stays on its repository.\n\nOperator only: needs a system-scoped token.",
        "operationId": "updateVault",
        "parameters": [
          {
            "description": "The vault's id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "description": "Object storage in this region. Manual backups may be kept up to 180 days.",
                "manual": {
                  "max_keep": "4320h",
                  "max_per_day": 4
                }
              },
              "schema": {
                "$ref": "#/components/schemas/VaultReq"
              }
            }
          },
          "description": "The fields to change. A field left out is kept, and so is a field left out of `manual`.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-09-12T09:30:00Z",
                  "description": "Object storage in this region. Manual backups may be kept up to 180 days.",
                  "id": "3d9f1b7e-2a4c-4e6b-8d1f-5a7c9e1b3d5f",
                  "immutable": false,
                  "manual": {
                    "keep_at_least": 86400000000000,
                    "keep_at_most": 15552000000000000,
                    "max_per_day": 4,
                    "until_deleted": true
                  },
                  "name": "Standard",
                  "public": true,
                  "repository_id": "8f3a2c1e-5d6b-4a7c-9e8f-1b2c3d4e5f60",
                  "repository_name": "objects-ro1",
                  "sentence": "A backup taken here by hand is kept from a day to 180 days, or until you delete it.",
                  "terms": {
                    "allowed": true,
                    "max_keep": 15552000000000000,
                    "max_per_day": 4,
                    "until_deleted": true
                  },
                  "updated_at": "2026-10-01T11:18:26Z"
                },
                "schema": {
                  "$ref": "#/components/schemas/VaultView"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Bad Request.\n\n`invalid_request`: The body is not a single JSON object, names a field this operation does not take, or gives a value of the wrong type."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unauthorized.\n\n`unauthenticated`: The token is missing, expired or not valid."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Forbidden.\n\n`forbidden`: The token lacks the `vault:write` permission."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Not Found.\n\n`not_found`: The id is not a UUID, or no vault has it."
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Conflict.\n\n`conflict`: Another vault already has the new name."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Request Entity Too Large.\n\n`body_too_large`: The body is larger than the service accepts."
          },
          "415": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unsupported Media Type.\n\n`unsupported_media_type`: The body is not sent as `application/json`."
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Unprocessable Entity.\n\n`validation_failed`: A field is not acceptable, and `fields` names each one. `repository_id` names another repository. `name` is empty or longer than 191 characters. The `manual` terms fail a check made on creation."
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "An error, as an RFC 7807 problem document."
          }
        },
        "summary": "Change a vault",
        "tags": [
          "Vaults"
        ],
        "x-access": "operator"
      }
    }
  },
  "security": [
    {
      "token": []
    }
  ],
  "tags": [
    {
      "description": "A plan protects servers, volumes or one bucket of a project under a protection policy. The operator writes the policy. It sets the schedule, the retention and the repositories backups are written to. The vault on the backup repository sets the terms for manual backups. The plan names what is protected, how consistent each backup is and how it is encrypted. Every scheduled run takes one backup of each resource, and restores start from those backups.",
      "name": "Plans"
    },
    {
      "description": "A policy is the operator's template for protection. It says when backups run, how long each is kept and which repository stores them. It can add a copy in a second repository, an instant tier, a recovery promise and a verification cadence. A project protects its servers, volumes and buckets with a plan that names a policy it is offered. Every backup the plan takes follows that policy. A policy is offered to every project (`public`) or only to the domains and projects it is assigned to (`assigned`).",
      "name": "Policies"
    },
    {
      "description": "A vault is how a repository is shown to projects. It has a name, a description and the terms for manual backups taken there. The terms say how long a manual backup may be kept, whether it may be kept until deleted, and how many may be taken a day. A policy keeps its backups in the vault of its backup repository. A manual backup outside any plan names a vault directly. Operators write vaults. A project sees the public ones and those of the policies it is offered.",
      "name": "Vaults"
    },
    {
      "description": "A backup is one resource's data at one instant: a server's disks, a volume, or a bucket's objects. A repository, registered by an operator, holds the data. A vault offers that repository to projects. A policy sets the schedule and the retention. A plan applies a policy to the project's resources and takes a backup of each one on schedule. A manual backup can be taken at any time, of a plan's resources or of one server or volume straight into a vault. Restores, attachments and file browsing all start from a backup.",
      "name": "Backups"
    },
    {
      "description": "A restore writes a backup back out, either as something new (a volume, a server or a bucket) or over the volume or server it was taken from. A new volume or server whose backup is still on an instant tier at this site is cloned from it in seconds and handed to the project, and its volumes are copied in full in the background. Every other restore reads the repository copy. A restore runs as a job. Poll the restore, or the job it names, until it succeeds or fails, then read `created_resources` for what it made. Attachments are kept with restores and appear in the restore list with `mode` `attach`.",
      "name": "Restores"
    },
    {
      "description": "An attachment presents one disk of a backup to a running server as a read-only volume, so files can be copied out with the server's own tools without restoring the whole backup. Behind it is a restore into a temporary volume, which is detached and deleted when the attachment is removed or expires.",
      "name": "Attachments"
    },
    {
      "description": "Browsing a backup's files without restoring it. A session gives a read-only view of one backup's disks: it finds and mounts their filesystems, lists directories and downloads single files, and ends at its deadline, after being left idle, or when it is closed. To bring back whole disks or servers, use Restores or Attachments instead. Errors raised while reading the filesystem itself, such as a path that does not exist or a directory given to download, keep their HTTP status but carry a body of the form `{\"error\": {\"code\": 404, \"message\": \"...\"}}` rather than a problem document.",
      "name": "File-level restore"
    },
    {
      "description": "A job is one unit of work the service runs: a backup of one resource, a restore, a verification, an expiry, a copy. Operations that start work answer with the job or its id. Read the job to follow its state, progress and events. Cancelling a job stops it and rolls nothing back.",
      "name": "Jobs"
    },
    {
      "description": "A preview of a protection policy's retention. For the backups the policy's plans already hold, it shows which the policy would keep, expire or find locked, and how much space expiring them would free. A proposed change to the policy can be previewed the same way. Nothing is changed. To apply a ladder, change the policy itself.",
      "name": "Retention"
    },
    {
      "description": "What the project could protect and what this cloud offers. The resource list names every server, volume and bucket of the project. For each it says whether a plan can protect it whole, in part, or not at all, and why. It is the same decision plan creation makes. Capabilities tell a form which choices plan creation would refuse before the form is filled in.",
      "name": "Resources"
    },
    {
      "description": "An operator's report of what the cloud is not protecting, per project and in total. It lists paused or unsatisfied plans, plans on a deprecated policy or on none, and missed backups. It also lists backups not yet copied or not immutably kept, resources no plan names, and quota problems. It reads across every plan and project, so it needs an operator token.",
      "name": "Coverage"
    },
    {
      "description": "What a project protects and what its backups occupy, in total and per repository. `stored_bytes`, the project's own deduplicated footprint, is the figure to bill on. It is measured by periodic sweeps of each repository and can lag behind the live counts.",
      "name": "Usage"
    },
    {
      "description": "A quota bounds how much a project may protect: the size of the resources under protection, how many resources, and the deduplicated bytes its backups occupy. Limits are set per project, per domain or as the cloud's defaults, and the most specific one applies. Going over a limit stops new protection and manual backups, never scheduled backups.",
      "name": "Quotas"
    },
    {
      "description": "A legal hold keeps backups beyond their expiry until an operator lifts it. It covers one backup, every backup of a plan, or every backup in a project. While a hold is active, expiry skips what it covers. Deleting a held backup, or a plan together with its backups, is refused.",
      "name": "Legal holds"
    },
    {
      "description": "A protection group names servers that recover together at a disaster recovery site, with a boot order and a health check per server. It names one protection policy, and that policy's recovery section is the group's promise: where it recovers, at which rung, how far behind and how fast. Every member must already be protected by a plan under that policy, because those plans' backups are what a recovery restores. In the region that would recover it, an operator registers a recovery of the group from the document its home writes into their shared repository, which binds every member to a project there. Where the policy's transport is `rbd-mirror` and the service can read Ceph mirroring, it samples the replication of the members' disks and marks the group degraded when it falls behind the policy's `rpo_target`. A group also records where its workload runs and which disaster recovery run is acting on it.",
      "name": "Protection groups"
    },
    {
      "description": "Moves a protection group's workload between regions and tests that it could be moved: a release where it runs, a recover where it goes, a test into an isolated network, and a resync or retire to clean up after one. Each is carried out as a run, which can be listed, watched, cancelled before its point of no return, aborted after it and resumed once halted. Also reports how well a group is protected, what a recovery of it would use and refuse right now, the sites a group can name and the isolated-network policies a test runs under, and lets a member of a group's project arm it with the standing delegation a run acts under. In this release an operator can recover a `cold` or `warm` group unplanned in the region that holds its standby copy, and a group can be tested there; either can be dry run first to see the plan it would follow. A planned recover, a release, a resync, a retire, a recovery at the group's home and the `hot` rung are refused with `dr_not_enabled` and change nothing. `GET /v1/capabilities` says under `dr` and `dr_reason` what runs on this cloud.",
      "name": "Disaster recovery"
    },
    {
      "description": "A standby is a copy of a protected resource's disks, kept in a pool and brought up to date after every new backup, so recovering from it takes about as long as a boot. An operator registers one directly, or through a recovery binding that names a standby pool. A project reads its own standbys and can turn a current one into volumes.",
      "name": "Standbys"
    },
    {
      "description": "Adopts backups that another cloud wrote into a repository this cloud can also read. An operator reads what a repository holds and binds a resource from the other cloud to a project here. That resource's backups then appear in the project and restore like its own. Recovery profiles translate the other cloud's flavors, networks and volume types into this cloud's. A dry run shows what a recovery would build before anything is created.",
      "name": "Recovery"
    },
    {
      "description": "A repository is storage the operator registers for backups to be written to. A chunk repository keeps deduplicated, compressed backups in an S3 bucket, a Ceph pool or a filesystem, encrypted unless a plan opts out. An rbd tier, the instant tier, also keeps recent backups as Ceph images, which restores of new volumes and servers are cloned from where this site serves it; every other restore reads the repository copy. A vault offers a repository to projects. Protection policies place their backups on repositories, plans follow policies, and every backup lives in a repository. This group also covers repository health and usage, the keys of each dedup domain, and the backup workers that move the data.",
      "name": "Repositories"
    },
    {
      "description": "Signing in, for the web console and for portals that embed it. A user name and password, or a token the caller already holds, is exchanged for a Keystone token scoped to one project or to the whole cloud. A portal that already holds a user's token can instead mint a one-time code that a browser redeems to be signed in without a password. Every other group takes the token these operations return, in `X-Auth-Token`.",
      "name": "Authentication"
    },
    {
      "description": "This document. It is generated by the service that serves it, so it always describes the release running there, and it needs no token.",
      "name": "OpenAPI document"
    }
  ],
  "servers": [
    {
      "url": "https://backup.mycompany.com",
      "description": "Ferry API"
    }
  ]
}
